[IMP] stock_orderpoint_manual_procurement: black, isort

This commit is contained in:
Joan Sisquella
2020-01-20 15:22:53 +01:00
committed by Hai Lang
parent c140bcc6ba
commit 2852918831
4 changed files with 178 additions and 176 deletions

View File

@@ -5,24 +5,19 @@
{
"name": "Stock Orderpoint Manual Procurement",
"summary": "Allows to create procurement orders from orderpoints instead "
"of relying only on the scheduler.",
"of relying only on the scheduler.",
"version": "12.0.1.1.0",
"author": "Eficent, "
"Odoo Community Association (OCA)",
"author": "Eficent, " "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"category": "Warehouse Management",
"depends": [
"purchase_stock",
],
"demo": [
"demo/product.xml",
],
"depends": ["purchase_stock"],
"demo": ["demo/product.xml"],
"data": [
"security/stock_orderpoint_manual_procurement_security.xml",
"wizards/make_procurement_orderpoint_view.xml",
"views/stock_warehouse_orderpoint_view.xml",
],
"license": "AGPL-3",
'installable': True,
'application': False,
"installable": True,
"application": False,
}

View File

@@ -2,26 +2,26 @@
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
from datetime import datetime
from odoo.addons import decimal_precision as dp
from odoo import api, fields, models
from odoo.tools import float_compare, float_round
from odoo.addons import decimal_precision as dp
UNIT = dp.get_precision('Product Unit of Measure')
UNIT = dp.get_precision("Product Unit of Measure")
class StockWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'
_inherit = "stock.warehouse.orderpoint"
procure_recommended_qty = fields.Float(
string='Procure Recommendation',
string="Procure Recommendation",
compute="_compute_procure_recommended",
digits=UNIT,
)
procure_recommended_date = fields.Date(
string='Recommended Request Date',
compute="_compute_procure_recommended",
string="Recommended Request Date", compute="_compute_procure_recommended"
)
@api.multi
@@ -29,21 +29,18 @@ class StockWarehouseOrderpoint(models.Model):
self.ensure_one()
procure_recommended_qty = 0.0
qty = max(self.product_min_qty, self.product_max_qty) - virtual_qty
remainder = \
self.qty_multiple > 0 and qty % self.qty_multiple or 0.0
if float_compare(
remainder, 0.0,
precision_rounding=self.product_uom.rounding) > 0:
remainder = self.qty_multiple > 0 and qty % self.qty_multiple or 0.0
if (
float_compare(remainder, 0.0, precision_rounding=self.product_uom.rounding)
> 0
):
qty += self.qty_multiple - remainder
if float_compare(
qty, 0.0,
precision_rounding=self.product_uom.rounding) <= 0:
if float_compare(qty, 0.0, precision_rounding=self.product_uom.rounding) <= 0:
return procure_recommended_qty
qty -= op_qtys[self.id]
qty_rounded = float_round(
qty, precision_rounding=self.product_uom.rounding)
qty_rounded = float_round(qty, precision_rounding=self.product_uom.rounding)
if qty_rounded > 0:
procure_recommended_qty = qty_rounded
return procure_recommended_qty
@@ -55,10 +52,16 @@ class StockWarehouseOrderpoint(models.Model):
for op in self:
qty = 0.0
virtual_qty = op.with_context(
location=op.location_id.id).product_id.virtual_available
if float_compare(virtual_qty, op.product_min_qty,
precision_rounding=op.product_uom.rounding) < 0:
location=op.location_id.id
).product_id.virtual_available
if (
float_compare(
virtual_qty,
op.product_min_qty,
precision_rounding=op.product_uom.rounding,
)
< 0
):
qty = op._get_procure_recommended_qty(virtual_qty, op_qtys)
op.procure_recommended_qty = qty
op.procure_recommended_date = op._get_date_planned(
qty, datetime.today())
op.procure_recommended_date = op._get_date_planned(qty, datetime.today())

View File

@@ -3,52 +3,54 @@
# Copyright 2016 Serpent Consulting Services Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo.tests import common
from odoo import fields
from datetime import timedelta
from odoo import fields
from odoo.tests import common
class TestStockWarehouseOrderpoint(common.TransactionCase):
def setUp(self):
super(TestStockWarehouseOrderpoint, self).setUp()
# Refs
self.group_stock_manager = self.env.ref('stock.group_stock_manager')
self.group_purchase_manager = self.env.ref(
'purchase.group_purchase_manager')
self.group_stock_manager = self.env.ref("stock.group_stock_manager")
self.group_purchase_manager = self.env.ref("purchase.group_purchase_manager")
self.group_change_procure_qty = self.env.ref(
'stock_orderpoint_manual_procurement.'
'group_change_orderpoint_procure_qty')
self.company1 = self.env.ref('base.main_company')
"stock_orderpoint_manual_procurement." "group_change_orderpoint_procure_qty"
)
self.company1 = self.env.ref("base.main_company")
# Get required Model
self.reordering_rule_model = self.env['stock.warehouse.orderpoint']
self.product_model = self.env['product.product']
self.purchase_model = self.env['purchase.order']
self.purchase_line_model = self.env['purchase.order.line']
self.user_model = self.env['res.users']
self.product_ctg_model = self.env['product.category']
self.stock_change_model = self.env['stock.change.product.qty']
self.make_procurement_orderpoint_model =\
self.env['make.procurement.orderpoint']
self.reordering_rule_model = self.env["stock.warehouse.orderpoint"]
self.product_model = self.env["product.product"]
self.purchase_model = self.env["purchase.order"]
self.purchase_line_model = self.env["purchase.order.line"]
self.user_model = self.env["res.users"]
self.product_ctg_model = self.env["product.category"]
self.stock_change_model = self.env["stock.change.product.qty"]
self.make_procurement_orderpoint_model = self.env["make.procurement.orderpoint"]
# Create users
self.user = self._create_user('user_1',
[self.group_stock_manager,
self.group_change_procure_qty,
self.group_purchase_manager],
self.company1)
self.user = self._create_user(
"user_1",
[
self.group_stock_manager,
self.group_change_procure_qty,
self.group_purchase_manager,
],
self.company1,
)
# Get required Model data
self.product_uom = self.env.ref('uom.product_uom_unit')
self.location = self.env.ref('stock.stock_location_stock')
self.product = self.env.ref('product.product_product_7')
self.product_uom = self.env.ref("uom.product_uom_unit")
self.location = self.env.ref("stock.stock_location_stock")
self.product = self.env.ref("product.product_product_7")
# Create vendor and supplier info
test_seller = self.env['res.partner'].create({'name': 'Test seller'})
self.vendor = self.env['product.supplierinfo'].create({
'name': test_seller.id,
'price': 8.0})
test_seller = self.env["res.partner"].create({"name": "Test seller"})
self.vendor = self.env["product.supplierinfo"].create(
{"name": test_seller.id, "price": 8.0}
)
# Create Product category and Product
self.product_ctg = self._create_product_category()
@@ -64,68 +66,76 @@ class TestStockWarehouseOrderpoint(common.TransactionCase):
def _create_user(self, login, groups, company):
""" Create a user."""
group_ids = [group.id for group in groups]
user = \
self.user_model.with_context({'no_reset_password': True}).create({
'name': 'Test User',
'login': login,
'password': 'demo',
'email': 'test@yourcompany.com',
'company_id': company.id,
'company_ids': [(4, company.id)],
'groups_id': [(6, 0, group_ids)]
})
user = self.user_model.with_context({"no_reset_password": True}).create(
{
"name": "Test User",
"login": login,
"password": "demo",
"email": "test@yourcompany.com",
"company_id": company.id,
"company_ids": [(4, company.id)],
"groups_id": [(6, 0, group_ids)],
}
)
return user
def _create_product_category(self):
"""Create a Product Category."""
product_ctg = self.product_ctg_model.create({
'name': 'test_product_ctg',
})
product_ctg = self.product_ctg_model.create({"name": "test_product_ctg"})
return product_ctg
def _create_product(self):
"""Create a Product."""
product = self.product_model.create({
'name': 'Test Product',
'categ_id': self.product_ctg.id,
'type': 'product',
'uom_id': self.product_uom.id,
'variant_seller_ids': [(6, 0, [self.vendor.id])],
})
product = self.product_model.create(
{
"name": "Test Product",
"categ_id": self.product_ctg.id,
"type": "product",
"uom_id": self.product_uom.id,
"variant_seller_ids": [(6, 0, [self.vendor.id])],
}
)
return product
def _update_product_qty(self, product, location, quantity):
"""Update Product quantity."""
change_product_qty = self.stock_change_model.create({
'location_id': location.id,
'product_id': product.id,
'new_quantity': quantity,
})
change_product_qty = self.stock_change_model.create(
{
"location_id": location.id,
"product_id": product.id,
"new_quantity": quantity,
}
)
change_product_qty.change_product_qty()
return change_product_qty
def create_orderpoint(self):
"""Create a Reordering Rule"""
reorder = self.reordering_rule_model.sudo(self.user).create({
'name': 'Order-point',
'product_id': self.product.id,
'product_min_qty': '100',
'product_max_qty': '500',
'qty_multiple': '1'
})
reorder = self.reordering_rule_model.sudo(self.user).create(
{
"name": "Order-point",
"product_id": self.product.id,
"product_min_qty": "100",
"product_max_qty": "500",
"qty_multiple": "1",
}
)
return reorder
def create_orderpoint_procurement(self, manual_date=None):
"""Make Procurement from Reordering Rule"""
context = {
'active_model': 'stock.warehouse.orderpoint',
'active_ids': self.reorder.ids,
'active_id': self.reorder.id
"active_model": "stock.warehouse.orderpoint",
"active_ids": self.reorder.ids,
"active_id": self.reorder.id,
}
wizard = self.make_procurement_orderpoint_model.sudo(self.user).\
with_context(context).create({})
wizard = (
self.make_procurement_orderpoint_model.sudo(self.user)
.with_context(context)
.create({})
)
if manual_date:
wizard.item_ids.write({'date_planned': manual_date})
wizard.item_ids.write({"date_planned": manual_date})
wizard.make_procurement()
return wizard
@@ -137,28 +147,26 @@ class TestStockWarehouseOrderpoint(common.TransactionCase):
# As per route configuration, it will create Purchase order
# Assert that Procurement is created with the desired quantity
purchase = self.purchase_model.search(
[('origin', 'ilike', self.reorder.name)])
purchase = self.purchase_model.search([("origin", "ilike", self.reorder.name)])
self.assertEquals(len(purchase), 1)
purchase_line = self.purchase_line_model.search(
[('orderpoint_id', '=', self.reorder.id),
('order_id', '=', purchase.id)])
[("orderpoint_id", "=", self.reorder.id), ("order_id", "=", purchase.id)]
)
self.assertEquals(len(purchase_line), 1)
self.assertNotEqual(self.reorder.procure_recommended_qty,
purchase_line.product_qty)
self.assertNotEqual(
self.reorder.procure_recommended_qty, purchase_line.product_qty
)
self.assertEqual(purchase_line.product_qty, 480.0)
def test_manual_procurement_modified_date(self):
"""Test manual procurement created from an orderpoint with
modified date."""
manual_date = fields.Date.from_string(
fields.Date.today()) + timedelta(days=30)
manual_date = fields.Date.from_string(fields.Date.today()) + timedelta(days=30)
self.create_orderpoint_procurement(manual_date)
purchase = self.purchase_model.search(
[('origin', 'ilike', self.reorder.name)])
purchase = self.purchase_model.search([("origin", "ilike", self.reorder.name)])
purchase_line = self.purchase_line_model.search(
[('orderpoint_id', '=', self.reorder.id),
('order_id', '=', purchase.id)])
[("orderpoint_id", "=", self.reorder.id), ("order_id", "=", purchase.id)]
)
self.assertEquals(len(purchase_line), 1)
pol_date = fields.Date.from_string(purchase_line.date_planned)
self.assertEquals(pol_date, manual_date)

View File

@@ -2,59 +2,61 @@
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models, _
from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
class MakeProcurementOrderpoint(models.TransientModel):
_name = 'make.procurement.orderpoint'
_description = 'Make Procurements from Orderpoints'
_name = "make.procurement.orderpoint"
_description = "Make Procurements from Orderpoints"
item_ids = fields.One2many(
'make.procurement.orderpoint.item',
'wiz_id', string='Items')
"make.procurement.orderpoint.item", "wiz_id", string="Items"
)
@api.model
def _prepare_item(self, orderpoint):
return {
'qty': orderpoint.procure_recommended_qty,
'qty_without_security': orderpoint.procure_recommended_qty,
'uom_id': orderpoint.product_uom.id,
'date_planned': orderpoint.procure_recommended_date, # string
'orderpoint_id': orderpoint.id,
'product_id': orderpoint.product_id.id,
'warehouse_id': orderpoint.warehouse_id.id,
'location_id': orderpoint.location_id.id
"qty": orderpoint.procure_recommended_qty,
"qty_without_security": orderpoint.procure_recommended_qty,
"uom_id": orderpoint.product_uom.id,
"date_planned": orderpoint.procure_recommended_date, # string
"orderpoint_id": orderpoint.id,
"product_id": orderpoint.product_id.id,
"warehouse_id": orderpoint.warehouse_id.id,
"location_id": orderpoint.location_id.id,
}
@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False,
submenu=False):
def fields_view_get(
self, view_id=None, view_type="form", toolbar=False, submenu=False
):
if not self.user_has_groups(
"stock_orderpoint_manual_procurement.group_change_orderpoint_procure_qty"): # noqa
"stock_orderpoint_manual_procurement.group_change_orderpoint_procure_qty"
): # noqa
# Redirect to readonly qty form view
view_id = self.env.ref(
'stock_orderpoint_manual_procurement.view_make_procure_without_security').id # noqa
"stock_orderpoint_manual_procurement.view_make_procure_without_security"
).id # noqa
return super(MakeProcurementOrderpoint, self).fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar,
submenu=submenu)
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
@api.model
def default_get(self, fields):
res = super(MakeProcurementOrderpoint, self).default_get(fields)
orderpoint_obj = self.env['stock.warehouse.orderpoint']
orderpoint_ids = self.env.context['active_ids'] or []
active_model = self.env.context['active_model']
orderpoint_obj = self.env["stock.warehouse.orderpoint"]
orderpoint_ids = self.env.context["active_ids"] or []
active_model = self.env.context["active_model"]
if not orderpoint_ids:
return res
assert active_model == 'stock.warehouse.orderpoint', \
'Bad context propagation'
assert active_model == "stock.warehouse.orderpoint", "Bad context propagation"
items = []
for line in orderpoint_obj.browse(orderpoint_ids):
items.append([0, 0, self._prepare_item(line)])
res['item_ids'] = items
res["item_ids"] = items
return res
@api.multi
@@ -63,16 +65,16 @@ class MakeProcurementOrderpoint(models.TransientModel):
errors = []
# User requesting the procurement is passed by context to be able to
# update final MO, PO or trasfer with that information.
pg_obj = self.env['procurement.group'].with_context(
requested_uid=self.env.user)
pg_obj = self.env["procurement.group"].with_context(requested_uid=self.env.user)
for item in self.item_ids:
if not item.qty:
raise ValidationError(_("Quantity must be positive."))
if not item.orderpoint_id:
raise ValidationError(_("No reordering rule found!"))
values = item.orderpoint_id._prepare_procurement_values(item.qty)
values['date_planned'] = fields.Datetime.to_string(
fields.Date.from_string(item.date_planned))
values["date_planned"] = fields.Datetime.to_string(
fields.Date.from_string(item.date_planned)
)
# Run procurement
try:
pg_obj.run(
@@ -82,55 +84,49 @@ class MakeProcurementOrderpoint(models.TransientModel):
item.orderpoint_id.location_id,
item.orderpoint_id.name,
item.orderpoint_id.name,
values
values,
)
except UserError as error:
errors.append(error.name)
errors.append(error.name)
if errors:
raise UserError('\n'.join(errors))
return {'type': 'ir.actions.act_window_close'}
raise UserError("\n".join(errors))
return {"type": "ir.actions.act_window_close"}
class MakeProcurementOrderpointItem(models.TransientModel):
_name = 'make.procurement.orderpoint.item'
_description = 'Make Procurements from Orderpoint Item'
_name = "make.procurement.orderpoint.item"
_description = "Make Procurements from Orderpoint Item"
wiz_id = fields.Many2one(
'make.procurement.orderpoint',
string='Wizard',
"make.procurement.orderpoint",
string="Wizard",
required=True,
ondelete='cascade',
readonly=True)
qty = fields.Float(
string='Qty')
qty_without_security = fields.Float(
string='Quantity')
uom_id = fields.Many2one(
string='Unit of Measure',
comodel_name='uom.uom')
date_planned = fields.Date(
string='Planned Date',
required=False)
ondelete="cascade",
readonly=True,
)
qty = fields.Float(string="Qty")
qty_without_security = fields.Float(string="Quantity")
uom_id = fields.Many2one(string="Unit of Measure", comodel_name="uom.uom")
date_planned = fields.Date(string="Planned Date", required=False)
orderpoint_id = fields.Many2one(
string='Reordering rule',
comodel_name='stock.warehouse.orderpoint',
readonly=False)
string="Reordering rule",
comodel_name="stock.warehouse.orderpoint",
readonly=False,
)
product_id = fields.Many2one(
string='Product',
comodel_name='product.product',
readonly=True)
string="Product", comodel_name="product.product", readonly=True
)
warehouse_id = fields.Many2one(
string='Warehouse',
comodel_name='stock.warehouse',
readonly=True)
string="Warehouse", comodel_name="stock.warehouse", readonly=True
)
location_id = fields.Many2one(
string='Location',
comodel_name='stock.location',
readonly=True)
string="Location", comodel_name="stock.location", readonly=True
)
@api.multi
@api.onchange('uom_id')
@api.onchange("uom_id")
def onchange_uom_id(self):
for rec in self:
rec.qty = rec.orderpoint_id.product_uom._compute_quantity(
rec.orderpoint_id.procure_recommended_qty, rec.uom_id)
rec.orderpoint_id.procure_recommended_qty, rec.uom_id
)