Add good support of unit of measures in purchase_suggest

This commit is contained in:
Alexis de Lattre
2016-03-01 17:35:39 +01:00
parent f2e48cb977
commit dba79c83c4
3 changed files with 47 additions and 22 deletions

View File

@@ -37,8 +37,6 @@ The advantage is that you are not impacted by the faulty procurements (for examp
You may want to increase the osv_memory_age_limit (default value = 1h) in Odoo server config file, in order to let some time to the purchase user to finish his work on the purchase suggestions. You may want to increase the osv_memory_age_limit (default value = 1h) in Odoo server config file, in order to let some time to the purchase user to finish his work on the purchase suggestions.
Warning: this module doesn't handle the case where a product uses multiple units of measures for the moment.
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>. This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""", """,
'author': 'Akretion', 'author': 'Akretion',

View File

@@ -112,17 +112,20 @@ class PurchaseSuggestionGenerate(models.TransientModel):
self.ensure_one() self.ensure_one()
pso = self.env['purchase.suggest'] pso = self.env['purchase.suggest']
polo = self.env['purchase.order.line'] polo = self.env['purchase.order.line']
puo = self.env['product.uom']
p_suggest_lines = [] p_suggest_lines = []
products = self.generate_products_dict() products = self.generate_products_dict()
# key = product_id # key = product_id
# value = {'virtual_qty': 1.0, 'draft_po_qty': 4.0, 'min_qty': 6.0} # value = {'virtual_qty': 1.0, 'draft_po_qty': 4.0, 'min_qty': 6.0}
# TODO : handle the uom # WARNING: draft_po_qty is in the UoM of the product
logger.info('Starting to compute the purchase suggestions') logger.info('Starting to compute the purchase suggestions')
logger.info('Min qty computed on %d products', len(products)) logger.info('Min qty computed on %d products', len(products))
polines = polo.search([ polines = polo.search([
('state', '=', 'draft'), ('product_id', 'in', products.keys())]) ('state', '=', 'draft'), ('product_id', 'in', products.keys())])
for line in polines: for line in polines:
products[line.product_id.id]['draft_po_qty'] += line.product_qty qty_product_po_uom = puo._compute_qty_obj(
line.product_uom, line.product_qty, line.product_id.uom_id)
products[line.product_id.id]['draft_po_qty'] += qty_product_po_uom
logger.info('Draft PO qty computed on %d products', len(products)) logger.info('Draft PO qty computed on %d products', len(products))
virtual_qties = self.pool['product.product']._product_available( virtual_qties = self.pool['product.product']._product_available(
self._cr, self._uid, products.keys(), self._cr, self._uid, products.keys(),
@@ -181,21 +184,32 @@ class PurchaseSuggest(models.TransientModel):
'res.company', string='Company', required=True) 'res.company', string='Company', required=True)
product_id = fields.Many2one( product_id = fields.Many2one(
'product.product', string='Product', required=True, readonly=True) 'product.product', string='Product', required=True, readonly=True)
uom_id = fields.Many2one(
'product.uom', string='UoM', related='product_id.uom_id',
readonly=True)
uom_po_id = fields.Many2one(
'product.uom', string='Purchase UoM', related='product_id.uom_po_id',
readonly=True)
seller_id = fields.Many2one( seller_id = fields.Many2one(
'res.partner', string='Supplier', readonly=True, 'res.partner', string='Supplier', readonly=True,
domain=[('supplier', '=', True)]) domain=[('supplier', '=', True)])
qty_available = fields.Float( qty_available = fields.Float(
string='Quantity On Hand', readonly=True, string='Quantity On Hand', readonly=True,
digits=dp.get_precision('Product Unit of Measure')) digits=dp.get_precision('Product Unit of Measure'),
help="in the unit of measure of the product")
incoming_qty = fields.Float( incoming_qty = fields.Float(
string='Incoming Quantity', readonly=True, string='Incoming Quantity', readonly=True,
digits=dp.get_precision('Product Unit of Measure')) digits=dp.get_precision('Product Unit of Measure'),
help="in the unit of measure of the product")
outgoing_qty = fields.Float( outgoing_qty = fields.Float(
string='Outgoing Quantity', readonly=True, string='Outgoing Quantity', readonly=True,
digits=dp.get_precision('Product Unit of Measure')) digits=dp.get_precision('Product Unit of Measure'),
help="in the unit of measure of the product")
draft_po_qty = fields.Float( draft_po_qty = fields.Float(
string='Draft PO Quantity', readonly=True, string='Draft PO Quantity', readonly=True,
digits=dp.get_precision('Product Unit of Measure')) digits=dp.get_precision('Product Unit of Measure'),
help="Draft purchase order quantity in the unit of measure "
"of the product (NOT in the purchase unit of measure !)")
last_po_line_id = fields.Many2one( last_po_line_id = fields.Many2one(
'purchase.order.line', string='Last Purchase Order Line', 'purchase.order.line', string='Last Purchase Order Line',
readonly=True) readonly=True)
@@ -206,6 +220,9 @@ class PurchaseSuggest(models.TransientModel):
related='last_po_line_id.product_qty', readonly=True, related='last_po_line_id.product_qty', readonly=True,
digits=dp.get_precision('Product Unit of Measure'), digits=dp.get_precision('Product Unit of Measure'),
string='Quantity of the Last Order') string='Quantity of the Last Order')
last_po_uom = fields.Many2one(
related='last_po_line_id.product_uom', readonly=True,
string='UoM of the Last Order')
orderpoint_id = fields.Many2one( orderpoint_id = fields.Many2one(
'stock.warehouse.orderpoint', string='Re-ordering Rule', 'stock.warehouse.orderpoint', string='Re-ordering Rule',
readonly=True) readonly=True)
@@ -213,10 +230,13 @@ class PurchaseSuggest(models.TransientModel):
'stock.location', string='Stock Location', readonly=True) 'stock.location', string='Stock Location', readonly=True)
min_qty = fields.Float( min_qty = fields.Float(
string="Min Quantity", readonly=True, string="Min Quantity", readonly=True,
digits=dp.get_precision('Product Unit of Measure')) digits=dp.get_precision('Product Unit of Measure'),
help="in the unit of measure for the product")
qty_to_order = fields.Float( qty_to_order = fields.Float(
string='Quantity to Order', string='Quantity to Order',
digits=dp.get_precision('Product Unit of Measure')) digits=dp.get_precision('Product Unit of Measure'),
help="Quantity to order in the purchase unit of measure for the "
"product")
class PurchaseSuggestPoCreate(models.TransientModel): class PurchaseSuggestPoCreate(models.TransientModel):
@@ -252,12 +272,13 @@ class PurchaseSuggestPoCreate(models.TransientModel):
po_vals.update(pick_type_dict['value']) po_vals.update(pick_type_dict['value'])
return po_vals return po_vals
def _prepare_purchase_order_line(self, partner, product, qty_to_order): def _prepare_purchase_order_line(
self, partner, product, qty_to_order, uom):
polo = self.env['purchase.order.line'] polo = self.env['purchase.order.line']
polnull = polo.browse(False) polnull = polo.browse(False)
product_change_res = polnull.onchange_product_id( product_change_res = polnull.onchange_product_id(
partner.property_product_pricelist_purchase.id, partner.property_product_pricelist_purchase.id,
product.id, qty_to_order, False, partner.id, product.id, qty_to_order, uom.id, partner.id,
fiscal_position_id=partner.property_account_position.id) fiscal_position_id=partner.property_account_position.id)
product_change_vals = product_change_res['value'] product_change_vals = product_change_res['value']
taxes_id_vals = [] taxes_id_vals = []
@@ -272,6 +293,7 @@ class PurchaseSuggestPoCreate(models.TransientModel):
self, partner, company, po_lines, location): self, partner, company, po_lines, location):
polo = self.env['purchase.order.line'] polo = self.env['purchase.order.line']
poo = self.env['purchase.order'] poo = self.env['purchase.order']
puo = self.env['product.uom']
existing_pos = poo.search([ existing_pos = poo.search([
('partner_id', '=', partner.id), ('partner_id', '=', partner.id),
('company_id', '=', company.id), ('company_id', '=', company.id),
@@ -281,16 +303,18 @@ class PurchaseSuggestPoCreate(models.TransientModel):
if existing_pos: if existing_pos:
# update the first existing PO # update the first existing PO
existing_po = existing_pos[0] existing_po = existing_pos[0]
for product, qty_to_order in po_lines: for product, qty_to_order, uom in po_lines:
existing_poline = polo.search([ existing_polines = polo.search([
('product_id', '=', product.id), ('product_id', '=', product.id),
('order_id', '=', existing_po.id), ('order_id', '=', existing_po.id),
]) ])
if existing_poline: if existing_polines:
existing_poline[0].product_qty += qty_to_order existing_poline = existing_polines[0]
existing_poline.product_qty += puo._compute_qty_obj(
uom, qty_to_order, existing_poline.product_uom)
else: else:
pol_vals = self._prepare_purchase_order_line( pol_vals = self._prepare_purchase_order_line(
partner, product, qty_to_order) partner, product, qty_to_order, uom)
pol_vals['order_id'] = existing_po.id pol_vals['order_id'] = existing_po.id
polo.create(pol_vals) polo.create(pol_vals)
existing_po.message_post( existing_po.message_post(
@@ -300,9 +324,9 @@ class PurchaseSuggestPoCreate(models.TransientModel):
# create new PO # create new PO
po_vals = self._prepare_purchase_order(partner, company, location) po_vals = self._prepare_purchase_order(partner, company, location)
order_lines = [] order_lines = []
for product, qty_to_order in po_lines: for product, qty_to_order, uom in po_lines:
pol_vals = self._prepare_purchase_order_line( pol_vals = self._prepare_purchase_order_line(
partner, product, qty_to_order) partner, product, qty_to_order, uom)
order_lines.append((0, 0, pol_vals)) order_lines.append((0, 0, pol_vals))
po_vals['order_line'] = order_lines po_vals['order_line'] = order_lines
new_po = poo.create(po_vals) new_po = poo.create(po_vals)
@@ -314,7 +338,7 @@ class PurchaseSuggestPoCreate(models.TransientModel):
# group by supplier # group by supplier
po_to_create = {} po_to_create = {}
# key = (seller, company) # key = (seller, company)
# value = [(product1, qty1), (product2, qty2)] # value = [(product1, qty1, uom1), (product2, qty2, uom2)]
psuggest_ids = self.env.context.get('active_ids') psuggest_ids = self.env.context.get('active_ids')
location = False location = False
for line in self.env['purchase.suggest'].browse(psuggest_ids): for line in self.env['purchase.suggest'].browse(psuggest_ids):
@@ -328,10 +352,10 @@ class PurchaseSuggestPoCreate(models.TransientModel):
% line.product_id.name) % line.product_id.name)
if (line.seller_id, line.company_id) in po_to_create: if (line.seller_id, line.company_id) in po_to_create:
po_to_create[(line.seller_id, line.company_id)].append( po_to_create[(line.seller_id, line.company_id)].append(
(line.product_id, line.qty_to_order)) (line.product_id, line.qty_to_order, line.uom_po_id))
else: else:
po_to_create[(line.seller_id, line.company_id)] = [ po_to_create[(line.seller_id, line.company_id)] = [
(line.product_id, line.qty_to_order)] (line.product_id, line.qty_to_order, line.uom_po_id)]
if not po_to_create: if not po_to_create:
raise Warning(_('No purchase orders created or updated')) raise Warning(_('No purchase orders created or updated'))
po_ids = [] po_ids = []

View File

@@ -60,9 +60,12 @@
<field name="outgoing_qty"/> <field name="outgoing_qty"/>
<field name="draft_po_qty"/> <field name="draft_po_qty"/>
<field name="min_qty"/> <field name="min_qty"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="last_po_date" widget="date"/> <field name="last_po_date" widget="date"/>
<field name="last_po_qty"/> <field name="last_po_qty"/>
<field name="last_po_uom" groups="product.group_uom"/>
<field name="qty_to_order"/> <field name="qty_to_order"/>
<field name="uom_po_id" groups="product.group_uom"/>
</tree> </tree>
</field> </field>
</record> </record>