From bcf77893afbe9724dbbcc6bb8479f1d1d6a8245c Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 10 Jul 2013 17:02:07 +0200 Subject: [PATCH] [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 --- addons/procurement/procurement.py | 10 +- addons/procurement/procurement_view.xml | 2 +- addons/sale/__openerp__.py | 2 +- addons/sale/sale.py | 194 +++++++++++++++ addons/sale_stock/__openerp__.py | 2 +- addons/sale_stock/sale_stock.py | 221 ++---------------- addons/stock/procurement.py | 1 + addons/stock_location/stock_location.py | 2 +- addons/stock_location_sale/__init__.py | 24 -- addons/stock_location_sale/__openerp__.py | 46 ---- .../stock_location_sale.py | 128 ---------- 11 files changed, 226 insertions(+), 406 deletions(-) delete mode 100644 addons/stock_location_sale/__init__.py delete mode 100644 addons/stock_location_sale/__openerp__.py delete mode 100644 addons/stock_location_sale/stock_location_sale.py diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index d3bb42208a7..1cf884dc78e 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -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'), diff --git a/addons/procurement/procurement_view.xml b/addons/procurement/procurement_view.xml index 0475be9ce63..ffb3642f6a7 100644 --- a/addons/procurement/procurement_view.xml +++ b/addons/procurement/procurement_view.xml @@ -83,7 +83,7 @@ - + diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index 8cc4919e3a1..a71f5f56c0b 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -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', diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 55bd34009bf..f99b19b5d55 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -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 = {} diff --git a/addons/sale_stock/__openerp__.py b/addons/sale_stock/__openerp__.py index df6b9ad1fdf..070d132a61d 100644 --- a/addons/sale_stock/__openerp__.py +++ b/addons/sale_stock/__openerp__.py @@ -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', diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 38a8d77ebbc..7c02e1b1ad6 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -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" diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py index ca1f5075ae7..1ad13b67aef 100644 --- a/addons/stock/procurement.py +++ b/addons/stock/procurement.py @@ -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, } diff --git a/addons/stock_location/stock_location.py b/addons/stock_location/stock_location.py index 1f5c25e11ca..674ae0d8768 100644 --- a/addons/stock_location/stock_location.py +++ b/addons/stock_location/stock_location.py @@ -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 diff --git a/addons/stock_location_sale/__init__.py b/addons/stock_location_sale/__init__.py deleted file mode 100644 index d9d11c54f81..00000000000 --- a/addons/stock_location_sale/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 . -# -############################################################################## - -import stock_location_sale - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_location_sale/__openerp__.py b/addons/stock_location_sale/__openerp__.py deleted file mode 100644 index 84fe0e7b704..00000000000 --- a/addons/stock_location_sale/__openerp__.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 . -# -############################################################################## - - -{ - '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: diff --git a/addons/stock_location_sale/stock_location_sale.py b/addons/stock_location_sale/stock_location_sale.py deleted file mode 100644 index 366af26f7e7..00000000000 --- a/addons/stock_location_sale/stock_location_sale.py +++ /dev/null @@ -1,128 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 . -# -############################################################################## - -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', - }