[WIP] Move from sale_stock what has only to do with procurement to sale and remove stock_location_sale
bzr revid: jco@openerp.com-20130710150207-imvyte86hftdd3pq
This commit is contained in:
parent
af7cf8a226
commit
bcf77893af
|
@ -105,12 +105,12 @@ class procurement_order(osv.osv):
|
|||
'group_id': fields.many2one('procurement.group', 'Procurement Requisition'),
|
||||
'rule_id': fields.many2one('procurement.rule', 'Rule'),
|
||||
|
||||
'product_id': fields.many2one('product.product', 'Product', required=True, states={'draft': [('readonly', False)]}, readonly=True),
|
||||
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, states={'draft': [('readonly', False)]}, readonly=True),
|
||||
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, states={'draft': [('readonly', False)]}, readonly=True),
|
||||
'product_id': fields.many2one('product.product', 'Product', required=True, states={'confirmed': [('readonly', False)]}, readonly=True),
|
||||
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, states={'confirmed': [('readonly', False)]}, readonly=True),
|
||||
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, states={'confirmed': [('readonly', False)]}, readonly=True),
|
||||
|
||||
'product_uos_qty': fields.float('UoS Quantity', states={'draft': [('readonly', False)]}, readonly=True),
|
||||
'product_uos': fields.many2one('product.uom', 'Product UoS', states={'draft': [('readonly', False)]}, readonly=True),
|
||||
'product_uos_qty': fields.float('UoS Quantity', states={'confirmed': [('readonly', False)]}, readonly=True),
|
||||
'product_uos': fields.many2one('product.uom', 'Product UoS', states={'confirmed': [('readonly', False)]}, readonly=True),
|
||||
|
||||
'state': fields.selection([
|
||||
('cancel', 'Cancelled'),
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
<field name="date_planned"/>
|
||||
<filter icon="terp-emblem-important" string="Exceptions" name="exceptions" domain="[('state','=','exception')]" help="Procurement Exceptions"/>
|
||||
<separator/>
|
||||
<filter icon="terp-gnome-cpu-frequency-applet+" string="Late" domain="['&', ('date_planned','<', current_date), ('state', 'in', ('draft', 'confirmed'))]" help="Procurement started late" />
|
||||
<filter icon="terp-gnome-cpu-frequency-applet+" string="Late" domain="['&', ('date_planned','<', current_date), ('state', '=', 'confirmed')]" help="Procurement started late" />
|
||||
<field name="product_id" />
|
||||
<field name="state" />
|
||||
<group expand="0" string="Group By">
|
||||
|
|
|
@ -59,7 +59,7 @@ The Dashboard for the Sales Manager will include
|
|||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/sale_dashboard.jpeg','images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'],
|
||||
'depends': ['account_voucher'],
|
||||
'depends': ['account_voucher', 'procurement'],
|
||||
'data': [
|
||||
'wizard/sale_make_invoice_advance.xml',
|
||||
'wizard/sale_line_invoice.xml',
|
||||
|
|
|
@ -624,6 +624,78 @@ class sale_order(osv.osv):
|
|||
|
||||
|
||||
|
||||
|
||||
def _prepare_order_line_procurement(self, cr, uid, order, line, group_id = False, context=None):
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers')
|
||||
date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
|
||||
return {
|
||||
'name': line.name,
|
||||
'origin': order.name,
|
||||
'date_planned': date_planned,
|
||||
'product_id': line.product_id.id,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': (line.product_uos and line.product_uos_qty)\
|
||||
or line.product_uom_qty,
|
||||
'product_uos': (line.product_uos and line.product_uos.id)\
|
||||
or line.product_uom.id,
|
||||
'location_id': location_id,
|
||||
'company_id': order.company_id.id,
|
||||
'note': line.name,
|
||||
'group_id': group_id,
|
||||
}
|
||||
|
||||
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
|
||||
start_date = self.date_to_datetime(cr, uid, start_date, context)
|
||||
date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=line.delay or 0.0)
|
||||
date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
return date_planned
|
||||
|
||||
def action_ship_create(self, cr, uid, ids, context=None):
|
||||
"""Create the required procurements to supply sales order lines, also connecting
|
||||
the procurements to appropriate stock moves in order to bring the goods to the
|
||||
sales order's requested location.
|
||||
|
||||
:param browse_record order: sales order to which the order lines belong
|
||||
:param list(browse_record) order_lines: sales order line records to procure
|
||||
:param int picking_id: optional ID of a stock picking to which the created stock moves
|
||||
will be added. A new picking will be created if omitted.
|
||||
:return: True
|
||||
"""
|
||||
procurement_obj = self.pool.get('procurement.order')
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
proc_ids = []
|
||||
group_id = self.pool.get("procurement.group").create(cr, uid, {'name': order.name}, context=context)
|
||||
for line in order.order_line:
|
||||
if (line.state == 'done') or not line.product_id:
|
||||
continue
|
||||
|
||||
proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context))
|
||||
proc_ids.append(proc_id)
|
||||
line.write({'procurement_id': proc_id})
|
||||
|
||||
procurement_obj.signal_button_confirm(cr, uid, proc_ids)
|
||||
|
||||
# FP NOTE: do we need this? isn't it the workflow that should set this
|
||||
val = {}
|
||||
if order.state == 'shipping_except':
|
||||
val['state'] = 'progress'
|
||||
val['shipped'] = False
|
||||
|
||||
if (order.order_policy == 'manual'):
|
||||
for line in order.order_line:
|
||||
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
|
||||
val['state'] = 'manual'
|
||||
break
|
||||
order.write(val)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# TODO add a field price_unit_uos
|
||||
# - update it on change product and unit price
|
||||
# - use it in report if there is a uos
|
||||
|
@ -664,6 +736,15 @@ class sale_order_line(osv.osv):
|
|||
WHERE rel.invoice_id = ANY(%s)""", (list(ids),))
|
||||
return [i[0] for i in cr.fetchall()]
|
||||
|
||||
def _number_packages(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
try:
|
||||
res[line.id] = int((line.product_uom_qty+line.product_packaging.qty-0.0001) / line.product_packaging.qty)
|
||||
except:
|
||||
res[line.id] = 1
|
||||
return res
|
||||
|
||||
_name = 'sale.order.line'
|
||||
_description = 'Sales Order Line'
|
||||
_columns = {
|
||||
|
@ -697,6 +778,12 @@ class sale_order_line(osv.osv):
|
|||
'order_partner_id': fields.related('order_id', 'partner_id', type='many2one', relation='res.partner', store=True, string='Customer'),
|
||||
'salesman_id':fields.related('order_id', 'user_id', type='many2one', relation='res.users', store=True, string='Salesperson'),
|
||||
'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
|
||||
'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation and the shipping of the products to the customer", readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'procurement_id': fields.many2one('procurement.order', 'Procurement'),
|
||||
#'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_packaging': fields.many2one('product.packaging', 'Packaging'),
|
||||
'number_packages': fields.function(_number_packages, type='integer', string='Number Packages'),
|
||||
|
||||
}
|
||||
_order = 'order_id desc, sequence, id'
|
||||
_defaults = {
|
||||
|
@ -708,6 +795,8 @@ class sale_order_line(osv.osv):
|
|||
'state': 'draft',
|
||||
'type': 'make_to_stock',
|
||||
'price_unit': 0.0,
|
||||
'delay': 0.0,
|
||||
'product_packaging': False,
|
||||
}
|
||||
|
||||
def _get_line_qty(self, cr, uid, line, context=None):
|
||||
|
@ -776,6 +865,111 @@ class sale_order_line(osv.osv):
|
|||
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False,
|
||||
partner_id=False, packaging=False, flag=False, context=None):
|
||||
if not product:
|
||||
return {'value': {'product_packaging': False}}
|
||||
product_obj = self.pool.get('product.product')
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
pack_obj = self.pool.get('product.packaging')
|
||||
warning = {}
|
||||
result = {}
|
||||
warning_msgs = ''
|
||||
if flag:
|
||||
res = self.product_id_change(cr, uid, ids, pricelist=pricelist,
|
||||
product=product, qty=qty, uom=uom, partner_id=partner_id,
|
||||
packaging=packaging, flag=False, context=context)
|
||||
warning_msgs = res.get('warning') and res['warning']['message']
|
||||
|
||||
products = product_obj.browse(cr, uid, product, context=context)
|
||||
if not products.packaging:
|
||||
packaging = result['product_packaging'] = False
|
||||
elif not packaging and products.packaging and not flag:
|
||||
packaging = products.packaging[0].id
|
||||
result['product_packaging'] = packaging
|
||||
|
||||
if packaging:
|
||||
default_uom = products.uom_id and products.uom_id.id
|
||||
pack = pack_obj.browse(cr, uid, packaging, context=context)
|
||||
q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom)
|
||||
# qty = qty - qty % q + q
|
||||
if qty and (q and not (qty % q) == 0):
|
||||
ean = pack.ean or _('(n/a)')
|
||||
qty_pack = pack.qty
|
||||
type_ul = pack.ul
|
||||
if not warning_msgs:
|
||||
warn_msg = _("You selected a quantity of %d Units.\n"
|
||||
"But it's not compatible with the selected packaging.\n"
|
||||
"Here is a proposition of quantities according to the packaging:\n"
|
||||
"EAN: %s Quantity: %s Type of ul: %s") % \
|
||||
(qty, ean, qty_pack, type_ul.name)
|
||||
warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n"
|
||||
warning = {
|
||||
'title': _('Configuration Error!'),
|
||||
'message': warning_msgs
|
||||
}
|
||||
result['product_uom_qty'] = qty
|
||||
|
||||
return {'value': result, 'warning': warning}
|
||||
|
||||
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
|
||||
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
|
||||
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
|
||||
context = context or {}
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
partner_obj = self.pool.get('res.partner')
|
||||
product_obj = self.pool.get('product.product')
|
||||
warning = {}
|
||||
res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty=qty,
|
||||
uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
|
||||
lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context)
|
||||
|
||||
if not product:
|
||||
res['value'].update({'product_packaging': False})
|
||||
return res
|
||||
|
||||
#update of result obtained in super function
|
||||
product_obj = product_obj.browse(cr, uid, product, context=context)
|
||||
res['value']['delay'] = (product_obj.sale_delay or 0.0)
|
||||
#res['value']['type'] = product_obj.procure_method
|
||||
|
||||
#check if product is available, and if not: raise an error
|
||||
uom2 = False
|
||||
if uom:
|
||||
uom2 = product_uom_obj.browse(cr, uid, uom)
|
||||
if product_obj.uom_id.category_id.id != uom2.category_id.id:
|
||||
uom = False
|
||||
if not uom2:
|
||||
uom2 = product_obj.uom_id
|
||||
|
||||
# Calling product_packaging_change function after updating UoM
|
||||
res_packing = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context)
|
||||
res['value'].update(res_packing.get('value', {}))
|
||||
warning_msgs = res_packing.get('warning') and res_packing['warning']['message'] or ''
|
||||
compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding)
|
||||
if (product_obj.type=='product') and int(compare_qty) == -1:
|
||||
#and (product_obj.procure_method=='make_to_stock'): --> need to find alternative for procure_method
|
||||
warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \
|
||||
(qty, uom2 and uom2.name or product_obj.uom_id.name,
|
||||
max(0,product_obj.virtual_available), product_obj.uom_id.name,
|
||||
max(0,product_obj.qty_available), product_obj.uom_id.name)
|
||||
warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n"
|
||||
|
||||
#update of warning messages
|
||||
if warning_msgs:
|
||||
warning = {
|
||||
'title': _('Configuration Error!'),
|
||||
'message' : warning_msgs
|
||||
}
|
||||
res.update({'warning': warning})
|
||||
return res
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def invoice_line_create(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
|
|
@ -57,7 +57,7 @@ You can choose flexible invoicing methods:
|
|||
'report/sale_report_view.xml',
|
||||
#'process/sale_stock_process.xml',
|
||||
],
|
||||
'demo_xml': ['sale_stock_demo.xml'],
|
||||
'demo_xml': ['sale_stock_demo.xml'],
|
||||
'test': ['test/cancel_order_sale_stock.yml',
|
||||
'test/picking_order_policy.yml',
|
||||
'test/prepaid_order_policy.yml',
|
||||
|
|
|
@ -37,6 +37,18 @@ class sale_order(osv.osv):
|
|||
})
|
||||
return super(sale_order, self).copy(cr, uid, id, default, context=context)
|
||||
|
||||
#Might have been deleted before for a reason
|
||||
def shipping_policy_change(self, cr, uid, ids, policy, context=None):
|
||||
if not policy:
|
||||
return {}
|
||||
inv_qty = 'order'
|
||||
if policy == 'prepaid':
|
||||
inv_qty = 'order'
|
||||
elif policy == 'picking':
|
||||
inv_qty = 'procurement'
|
||||
return {'value': {'invoice_quantity': inv_qty}}
|
||||
|
||||
|
||||
def _get_default_warehouse(self, cr, uid, context=None):
|
||||
company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
|
||||
warehouse_ids = self.pool.get('stock.warehouse').search(cr, uid, [('company_id', '=', company_id)], context=context)
|
||||
|
@ -139,7 +151,7 @@ class sale_order(osv.osv):
|
|||
for mov in pick.move_lines:
|
||||
proc_ids = proc_obj.search(cr, uid, [('move_id', '=', mov.id)])
|
||||
if proc_ids:
|
||||
proc_obj.signal_button_check(cr, uid, proc_ids)
|
||||
proc_obj.signal_button_check(cr, uid, proc_ids)
|
||||
for r in self.read(cr, uid, ids, ['picking_ids']):
|
||||
stock_obj.signal_button_cancel(cr, uid, r['picking_ids'])
|
||||
return super(sale_order, self).action_cancel(cr, uid, ids, context=context)
|
||||
|
@ -152,13 +164,6 @@ class sale_order(osv.osv):
|
|||
self.write(cr, uid, [o.id], {'order_policy': 'manual'}, context=context)
|
||||
return res
|
||||
|
||||
def procurement_lines_get(self, cr, uid, ids, *args):
|
||||
res = []
|
||||
for order in self.browse(cr, uid, ids, context={}):
|
||||
for line in order.order_line:
|
||||
if line.procurement_id:
|
||||
res.append(line.procurement_id.id)
|
||||
return res
|
||||
|
||||
def date_to_datetime(self, cr, uid, userdate, context=None):
|
||||
""" Convert date values expressed in user's timezone to
|
||||
|
@ -215,81 +220,6 @@ class sale_order(osv.osv):
|
|||
elif mode == 'canceled':
|
||||
return canceled
|
||||
|
||||
def _prepare_order_line_procurement(self, cr, uid, order, line, group_id = False, context=None):
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers')
|
||||
date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
|
||||
return {
|
||||
'name': line.name,
|
||||
'origin': order.name,
|
||||
'date_planned': date_planned,
|
||||
'product_id': line.product_id.id,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': (line.product_uos and line.product_uos_qty)\
|
||||
or line.product_uom_qty,
|
||||
'product_uos': (line.product_uos and line.product_uos.id)\
|
||||
or line.product_uom.id,
|
||||
'location_id': location_id,
|
||||
'procure_method': line.type,
|
||||
'move_id': move_id,
|
||||
'company_id': order.company_id.id,
|
||||
'note': line.name,
|
||||
'group_id': group_id,
|
||||
}
|
||||
|
||||
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
|
||||
start_date = self.date_to_datetime(cr, uid, start_date, context)
|
||||
date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=line.delay or 0.0)
|
||||
date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
return date_planned
|
||||
|
||||
def action_ship_create(self, cr, uid, ids, context=None):
|
||||
"""Create the required procurements to supply sales order lines, also connecting
|
||||
the procurements to appropriate stock moves in order to bring the goods to the
|
||||
sales order's requested location.
|
||||
|
||||
If ``picking_id`` is provided, the stock moves will be added to it, otherwise
|
||||
a standard outgoing picking will be created to wrap the stock moves, as returned
|
||||
by :meth:`~._prepare_order_picking`.
|
||||
|
||||
Modules that wish to customize the procurements or partition the stock moves over
|
||||
multiple stock pickings may override this method and call ``super()`` with
|
||||
different subsets of ``order_lines`` and/or preset ``picking_id`` values.
|
||||
|
||||
:param browse_record order: sales order to which the order lines belong
|
||||
:param list(browse_record) order_lines: sales order line records to procure
|
||||
:param int picking_id: optional ID of a stock picking to which the created stock moves
|
||||
will be added. A new picking will be created if ommitted.
|
||||
:return: True
|
||||
"""
|
||||
procurement_obj = self.pool.get('procurement.order')
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
proc_ids = []
|
||||
group_id = self.pool.get("procurement.group").create(cr, uid, {'name': order.name}, context=context)
|
||||
for line in order.order_lines:
|
||||
if (line.state == 'done') or not line.product_id:
|
||||
continue
|
||||
|
||||
proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context))
|
||||
proc_ids.append(proc_id)
|
||||
line.write({'procurement_id': proc_id})
|
||||
|
||||
procurement_obj.signal_button_confirm(cr, uid, proc_ids)
|
||||
|
||||
# FP NOTE: do we need this? isn't it the workflow that should set this
|
||||
val = {}
|
||||
if order.state == 'shipping_except':
|
||||
val['state'] = 'progress'
|
||||
val['shipped'] = False
|
||||
|
||||
if (order.order_policy == 'manual'):
|
||||
for line in order.order_line:
|
||||
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
|
||||
val['state'] = 'manual'
|
||||
break
|
||||
order.write(val)
|
||||
return True
|
||||
|
||||
def action_ship_end(self, cr, uid, ids, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
|
@ -316,6 +246,14 @@ class sale_order(osv.osv):
|
|||
if order_line.product_id and order_line.product_id.type in ('product', 'consu'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def procurement_lines_get(self, cr, uid, ids, *args):
|
||||
res = []
|
||||
for order in self.browse(cr, uid, ids, context={}):
|
||||
for line in order.order_line:
|
||||
if line.procurement_id:
|
||||
res.append(line.procurement_id.id)
|
||||
return res
|
||||
|
||||
class stock_move(osv.osv):
|
||||
_inherit = 'stock.move'
|
||||
|
@ -326,27 +264,11 @@ class stock_move(osv.osv):
|
|||
|
||||
class sale_order_line(osv.osv):
|
||||
|
||||
def _number_packages(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
try:
|
||||
res[line.id] = int((line.product_uom_qty+line.product_packaging.qty-0.0001) / line.product_packaging.qty)
|
||||
except:
|
||||
res[line.id] = 1
|
||||
return res
|
||||
|
||||
|
||||
_inherit = 'sale.order.line'
|
||||
_columns = {
|
||||
'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation and the shipping of the products to the customer", readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'procurement_id': fields.many2one('procurement.order', 'Procurement'),
|
||||
#'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_packaging': fields.many2one('product.packaging', 'Packaging'),
|
||||
'move_ids': fields.one2many('stock.move', 'sale_line_id', 'Inventory Moves', readonly=True),
|
||||
'number_packages': fields.function(_number_packages, type='integer', string='Number Packages'),
|
||||
}
|
||||
_defaults = {
|
||||
'delay': 0.0,
|
||||
'product_packaging': False,
|
||||
}
|
||||
|
||||
def button_cancel(self, cr, uid, ids, context=None):
|
||||
|
@ -365,105 +287,6 @@ class sale_order_line(osv.osv):
|
|||
default.update({'move_ids': []})
|
||||
return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context)
|
||||
|
||||
def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False,
|
||||
partner_id=False, packaging=False, flag=False, context=None):
|
||||
if not product:
|
||||
return {'value': {'product_packaging': False}}
|
||||
product_obj = self.pool.get('product.product')
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
pack_obj = self.pool.get('product.packaging')
|
||||
warning = {}
|
||||
result = {}
|
||||
warning_msgs = ''
|
||||
if flag:
|
||||
res = self.product_id_change(cr, uid, ids, pricelist=pricelist,
|
||||
product=product, qty=qty, uom=uom, partner_id=partner_id,
|
||||
packaging=packaging, flag=False, context=context)
|
||||
warning_msgs = res.get('warning') and res['warning']['message']
|
||||
|
||||
products = product_obj.browse(cr, uid, product, context=context)
|
||||
if not products.packaging:
|
||||
packaging = result['product_packaging'] = False
|
||||
elif not packaging and products.packaging and not flag:
|
||||
packaging = products.packaging[0].id
|
||||
result['product_packaging'] = packaging
|
||||
|
||||
if packaging:
|
||||
default_uom = products.uom_id and products.uom_id.id
|
||||
pack = pack_obj.browse(cr, uid, packaging, context=context)
|
||||
q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom)
|
||||
# qty = qty - qty % q + q
|
||||
if qty and (q and not (qty % q) == 0):
|
||||
ean = pack.ean or _('(n/a)')
|
||||
qty_pack = pack.qty
|
||||
type_ul = pack.ul
|
||||
if not warning_msgs:
|
||||
warn_msg = _("You selected a quantity of %d Units.\n"
|
||||
"But it's not compatible with the selected packaging.\n"
|
||||
"Here is a proposition of quantities according to the packaging:\n"
|
||||
"EAN: %s Quantity: %s Type of ul: %s") % \
|
||||
(qty, ean, qty_pack, type_ul.name)
|
||||
warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n"
|
||||
warning = {
|
||||
'title': _('Configuration Error!'),
|
||||
'message': warning_msgs
|
||||
}
|
||||
result['product_uom_qty'] = qty
|
||||
|
||||
return {'value': result, 'warning': warning}
|
||||
|
||||
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
|
||||
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
|
||||
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
|
||||
context = context or {}
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
partner_obj = self.pool.get('res.partner')
|
||||
product_obj = self.pool.get('product.product')
|
||||
warning = {}
|
||||
res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty=qty,
|
||||
uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
|
||||
lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context)
|
||||
|
||||
if not product:
|
||||
res['value'].update({'product_packaging': False})
|
||||
return res
|
||||
|
||||
#update of result obtained in super function
|
||||
product_obj = product_obj.browse(cr, uid, product, context=context)
|
||||
res['value']['delay'] = (product_obj.sale_delay or 0.0)
|
||||
res['value']['type'] = product_obj.procure_method
|
||||
|
||||
#check if product is available, and if not: raise an error
|
||||
uom2 = False
|
||||
if uom:
|
||||
uom2 = product_uom_obj.browse(cr, uid, uom)
|
||||
if product_obj.uom_id.category_id.id != uom2.category_id.id:
|
||||
uom = False
|
||||
if not uom2:
|
||||
uom2 = product_obj.uom_id
|
||||
|
||||
# Calling product_packaging_change function after updating UoM
|
||||
res_packing = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context)
|
||||
res['value'].update(res_packing.get('value', {}))
|
||||
warning_msgs = res_packing.get('warning') and res_packing['warning']['message'] or ''
|
||||
compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding)
|
||||
if (product_obj.type=='product') and int(compare_qty) == -1 \
|
||||
and (product_obj.procure_method=='make_to_stock'):
|
||||
warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \
|
||||
(qty, uom2 and uom2.name or product_obj.uom_id.name,
|
||||
max(0,product_obj.virtual_available), product_obj.uom_id.name,
|
||||
max(0,product_obj.qty_available), product_obj.uom_id.name)
|
||||
warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n"
|
||||
|
||||
#update of warning messages
|
||||
if warning_msgs:
|
||||
warning = {
|
||||
'title': _('Configuration Error!'),
|
||||
'message' : warning_msgs
|
||||
}
|
||||
res.update({'warning': warning})
|
||||
return res
|
||||
|
||||
|
||||
class sale_advance_payment_inv(osv.osv_memory):
|
||||
_inherit = "sale.advance.payment.inv"
|
||||
|
|
|
@ -77,6 +77,7 @@ class procurement_order(osv.osv):
|
|||
'location_id': procurement.rule_id.location_src_id.id,
|
||||
'location_dest_id': procurement.rule_id.location_id.id,
|
||||
'move_dest_id': procurement.move_dest_id and procurement.move_dest_id.id or False,
|
||||
'procure_method': procurement.rule_id and procurement.rule_id.procure_method or 'make_to_stock',
|
||||
#'cancel_cascade': procurement.rule_id and procurement.rule_id.cancel_cascade or False,
|
||||
'group_id': procurement.group_id and procurement.group_id.id or False,
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ class procurement_order(osv.osv):
|
|||
date = procurement.date_planned
|
||||
newdate = (datetime.strptime(date, '%Y-%m-%d %H:%M:%S') - relativedelta(days=procurement.rule_id.delay or 0)).strftime('%Y-%m-%d %H:%M:%S')
|
||||
d.update({
|
||||
'date_planned': newdate,
|
||||
'date': newdate,
|
||||
})
|
||||
return d
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import stock_location_sale
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -1,46 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
{
|
||||
'name': 'Advanced Routes with sales',
|
||||
'version': '1.0',
|
||||
'category': 'Manufacturing',
|
||||
'description': """
|
||||
Cross-module for stock_location and sale such that a sale order generates a procurement in stock instead
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'images': [],
|
||||
'depends': ['sale', 'stock_location'],
|
||||
'data': [],
|
||||
'demo': [
|
||||
# 'stock_location_demo_cpu1.xml',
|
||||
# 'stock_location_demo_cpu3.yml',
|
||||
],
|
||||
'installable': True,
|
||||
'test': [
|
||||
# 'test/stock_location_pull_flow.yml',
|
||||
# 'test/stock_location_push_flow.yml',
|
||||
],
|
||||
'auto_install': False,
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -1,128 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class sale_order(osv.osv):
|
||||
_inherit = "sale.order"
|
||||
|
||||
def action_ship_create(self, cr, uid, ids, context=None):
|
||||
print "acitonshipcreate"
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
self._create_pickings_and_procurements(cr, uid, order, order.order_line, None, context=context)
|
||||
return True
|
||||
|
||||
|
||||
def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None):
|
||||
"""Create the required procurements to supply sales order lines, also connecting
|
||||
the procurements to appropriate stock moves in order to bring the goods to the
|
||||
sales order's requested location.
|
||||
|
||||
If ``picking_id`` is provided, the stock moves will be added to it, otherwise
|
||||
a standard outgoing picking will be created to wrap the stock moves, as returned
|
||||
by :meth:`~._prepare_order_picking`.
|
||||
|
||||
Modules that wish to customize the procurements or partition the stock moves over
|
||||
multiple stock pickings may override this method and call ``super()`` with
|
||||
different subsets of ``order_lines`` and/or preset ``picking_id`` values.
|
||||
|
||||
:param browse_record order: sales order to which the order lines belong
|
||||
:param list(browse_record) order_lines: sales order line records to procure
|
||||
:param int picking_id: optional ID of a stock picking to which the created stock moves
|
||||
will be added. A new picking will be created if omitted.
|
||||
:return: True
|
||||
"""
|
||||
print "Create pickings and procurements!"
|
||||
move_obj = self.pool.get('stock.move')
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
procurement_obj = self.pool.get('procurement.order')
|
||||
proc_ids = []
|
||||
|
||||
#Create group
|
||||
group_id = self.pool.get("procurement.group").create(cr, uid, {'name': order.name}, context=context)
|
||||
|
||||
for line in order_lines:
|
||||
if line.state == 'done':
|
||||
continue
|
||||
|
||||
date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
|
||||
|
||||
# if line.product_id:
|
||||
# if line.product_id.type in ('product', 'consu'):
|
||||
# if not picking_id:
|
||||
# picking_id = picking_obj.create(cr, uid, self._prepare_order_picking(cr, uid, order, context=context))
|
||||
# #move_id = move_obj.create(cr, uid, self._prepare_order_line_move(cr, uid, order, line, picking_id, date_planned, group_id=group_id, context=context))
|
||||
# else:
|
||||
# # a service has no stock move
|
||||
# move_id = False
|
||||
|
||||
#TODO Need to do something instead of warehouse
|
||||
if line.product_id:
|
||||
proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, [], date_planned, group_id = group_id, context=context))
|
||||
proc_ids.append(proc_id)
|
||||
line.write({'procurement_id': proc_id})
|
||||
#self.ship_recreate(cr, uid, order, line, move_id, proc_id)
|
||||
|
||||
# if picking_id:
|
||||
# picking_obj.signal_button_confirm(cr, uid, [picking_id])
|
||||
procurement_obj.signal_button_confirm(cr, uid, proc_ids)
|
||||
|
||||
val = {}
|
||||
if order.state == 'shipping_except':
|
||||
val['state'] = 'progress'
|
||||
val['shipped'] = False
|
||||
|
||||
if (order.order_policy == 'manual'):
|
||||
for line in order.order_line:
|
||||
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
|
||||
val['state'] = 'manual'
|
||||
break
|
||||
order.write(val)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, group_id = False, context=None):
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers')
|
||||
output_id = order.warehouse_id.lot_output_id.id
|
||||
return {
|
||||
'name': line.name,
|
||||
'origin': order.name,
|
||||
'date_planned': date_planned,
|
||||
'product_id': line.product_id.id,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': (line.product_uos and line.product_uos_qty)\
|
||||
or line.product_uom_qty,
|
||||
'product_uos': (line.product_uos and line.product_uos.id)\
|
||||
or line.product_uom.id,
|
||||
'location_id': location_id,
|
||||
'procure_method': line.type,
|
||||
'move_id': move_id,
|
||||
'company_id': order.company_id.id,
|
||||
'note': line.name,
|
||||
'group_id': group_id,
|
||||
'state': 'draft',
|
||||
}
|
Loading…
Reference in New Issue