[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:
Josse Colpaert 2013-07-10 17:02:07 +02:00
parent af7cf8a226
commit bcf77893af
11 changed files with 226 additions and 406 deletions

View File

@ -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'),

View File

@ -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="['&amp;', ('date_planned','&lt;', current_date), ('state', 'in', ('draft', 'confirmed'))]" help="Procurement started late" />
<filter icon="terp-gnome-cpu-frequency-applet+" string="Late" domain="['&amp;', ('date_planned','&lt;', current_date), ('state', '=', 'confirmed')]" help="Procurement started late" />
<field name="product_id" />
<field name="state" />
<group expand="0" string="Group By">

View File

@ -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',

View File

@ -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 = {}

View File

@ -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',

View File

@ -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"

View File

@ -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,
}

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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',
}