diff --git a/addons/sale_order_dates/__openerp__.py b/addons/sale_order_dates/__openerp__.py index 3fa9fba0ecf..1f4eb399dd8 100644 --- a/addons/sale_order_dates/__openerp__.py +++ b/addons/sale_order_dates/__openerp__.py @@ -22,7 +22,7 @@ { 'name': 'Dates on Sales Order', - 'version': '1.0', + 'version': '1.1', 'category': 'Sales Management', 'description': """ Add additional date information to the sales order. @@ -30,7 +30,7 @@ Add additional date information to the sales order. You can add the following additional dates to a sales order: ------------------------------------------------------------ - * Requested Date + * Requested Date (will be used as the expected date on pickings) * Commitment Date * Effective Date """, @@ -40,7 +40,7 @@ You can add the following additional dates to a sales order: 'depends': ['sale_stock'], 'data': ['sale_order_dates_view.xml'], 'demo': [], - 'test': [], + 'test': ['test/requested_date.yml'], 'installable': True, 'auto_install': False, } diff --git a/addons/sale_order_dates/i18n/sale_order_dates.pot b/addons/sale_order_dates/i18n/sale_order_dates.pot index 8a45a8a46af..92b536c973e 100644 --- a/addons/sale_order_dates/i18n/sale_order_dates.pot +++ b/addons/sale_order_dates/i18n/sale_order_dates.pot @@ -32,12 +32,14 @@ msgstr "" #. module: sale_order_dates #: help:sale.order,effective_date:0 -msgid "Date on which picking is created." +msgid "Date on which the first Delivery Order was created." msgstr "" #. module: sale_order_dates #: help:sale.order,requested_date:0 -msgid "Date requested by the customer for the sale." +msgid "Date by which the customer has requested the items to be delivered.\n" +"When this Order gets confirmed, the Delivery Order's expected date will be computed based on this date and the Company's Security Delay.\n" +"Leave this field empty if you want the Delivery Order to be processed as soon as possible. In that case the expected date will be computed using the default method: based on the Product Lead Times and the Company's Security Delay." msgstr "" #. module: sale_order_dates @@ -52,6 +54,18 @@ msgstr "" #. module: sale_order_dates #: help:sale.order,commitment_date:0 -msgid "Committed date for delivery." +msgid "Date by which the products is sure to be delivered. This is a date that you can promise to the customer, based on the Product Lead Times." +msgstr "" + +#. module: sale_order_dates +#: code:addons/sale_order_dates/sale_order_dates.py:90 +#, python-format +msgid "Requested date is too soon!" +msgstr "" + +#. module: sale_order_dates +#: code:addons/sale_order_dates/sale_order_dates.py:91 +#, python-format +msgid "The date requested by the customer is sooner than the commitment date. You may be unable to honor the customer's request." msgstr "" diff --git a/addons/sale_order_dates/sale_order_dates.py b/addons/sale_order_dates/sale_order_dates.py index 64b939de088..11433911c45 100644 --- a/addons/sale_order_dates/sale_order_dates.py +++ b/addons/sale_order_dates/sale_order_dates.py @@ -19,15 +19,40 @@ # ############################################################################## -from datetime import datetime -from dateutil.relativedelta import relativedelta +from datetime import datetime, timedelta from openerp.osv import fields, osv +from openerp.tools.translate import _ +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT class sale_order_dates(osv.osv): + """Add several date fields to Sale Orders, computed or user-entered""" _inherit = 'sale.order' + def copy(self, cr, uid, id, default=None, context=None): + """Don't copy the requested date along with the Sale Order""" + if default is None: + default = {} + else: + default = default.copy() + default['requested_date'] = False + return super(sale_order_dates, self).copy(cr, uid, id, default=default, + context=context) + + def _order_line_move_date(self, cr, uid, line, context=None): + """Compute the expected date from the requested date, not the order date""" + order=line.order_id + if order and order.requested_date: + date_planned = datetime.strptime(order.requested_date, + DEFAULT_SERVER_DATE_FORMAT) + date_planned -= timedelta(days=order.company_id.security_lead) + return date_planned.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + else: + return super(sale_order_dates, self)._order_line_move_date(cr, uid, line) + def _get_effective_date(self, cr, uid, ids, name, arg, context=None): + """Read the shipping date from the related packings""" + # XXX would be better if it returned the date the picking was processed res = {} dates_list = [] for order in self.browse(cr, uid, ids, context=context): @@ -41,22 +66,56 @@ class sale_order_dates(osv.osv): return res def _get_commitment_date(self, cr, uid, ids, name, arg, context=None): + """Compute the commitment date""" res = {} dates_list = [] for order in self.browse(cr, uid, ids, context=context): dates_list = [] for line in order.order_line: - dt = datetime.strptime(order.date_order, '%Y-%m-%d') + relativedelta(days=line.delay or 0.0) - dt_s = dt.strftime('%Y-%m-%d') + dt = (datetime.strptime(order.date_order, + DEFAULT_SERVER_DATE_FORMAT) + + timedelta(days=line.delay or 0.0) ) + dt_s = dt.strftime(DEFAULT_SERVER_DATE_FORMAT) dates_list.append(dt_s) if dates_list: res[order.id] = min(dates_list) return res + def onchange_requested_date(self, cr, uid, ids, requested_date, + commitment_date, context=None): + """Warn if the requested dates is sooner than the commitment date""" + if (requested_date and commitment_date + and requested_date < commitment_date): + return {'warning': { + 'title': _('Requested date is too soon!'), + 'message': _("The date requested by the customer is " + "sooner than the commitment date. You may be " + "unable to honor the customer's request.") + } + } + else: + return {} + _columns = { - 'commitment_date': fields.function(_get_commitment_date, store=True, type='date', string='Commitment Date', help="Committed date for delivery."), - 'requested_date': fields.date('Requested Date', help="Date requested by the customer for the sale."), - 'effective_date': fields.function(_get_effective_date, type='date', store=True, string='Effective Date',help="Date on which picking is created."), + 'commitment_date': fields.function(_get_commitment_date, store=True, + type='date', string='Commitment Date', + help="Date by which the products is sure to be delivered. This is " + "a date that you can promise to the customer, based on the " + "Product Lead Times."), + 'requested_date': fields.date('Requested Date', + readonly=True, states={'draft': [('readonly', False)]}, + help="Date by which the customer has requested the items to be " + "delivered.\n" + "When this Order gets confirmed, the Delivery Order's " + "expected date will be computed based on this date and the " + "Company's Security Delay.\n" + "Leave this field empty if you want the Delivery Order to be " + "processed as soon as possible. In that case the expected " + "date will be computed using the default method: based on " + "the Product Lead Times and the Company's Security Delay."), + 'effective_date': fields.function(_get_effective_date, type='date', + store=True, string='Effective Date', + help="Date on which the first Delivery Order was created."), } diff --git a/addons/sale_order_dates/sale_order_dates_view.xml b/addons/sale_order_dates/sale_order_dates_view.xml index 68419cd29bb..0de42895f5b 100644 --- a/addons/sale_order_dates/sale_order_dates_view.xml +++ b/addons/sale_order_dates/sale_order_dates_view.xml @@ -10,13 +10,25 @@ - + + + sale.order.tree.inherit5 + sale.order + tree + + + + + + + + diff --git a/addons/sale_order_dates/test/requested_date.yml b/addons/sale_order_dates/test/requested_date.yml new file mode 100644 index 00000000000..cdeec445409 --- /dev/null +++ b/addons/sale_order_dates/test/requested_date.yml @@ -0,0 +1,31 @@ +- + In order to test the Requested Date feature in Sale Orders in OpenERP, + I update a demo Sale Order with Requested Date on 2010-12-17 +- + !python {model: sale.order}: | + so = self.write(cr, uid, ref("sale.order"), {'requested_date': '2010-07-12'}) +- + I confirm the Sale Order. +- + !workflow { + model: sale.order, action: order_confirm, + ref: sale.order + } +- + I verify that the Procurements and Stock Moves have been generated with the + correct date +- + !python {model: sale.order}: | + from datetime import datetime, timedelta + from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT + + so = self.browse(cr, uid, ref("sale.order")) + security_delay = timedelta(days=so.shop_id.company_id.security_lead) + requested_date = datetime.strptime(so.requested_date, + DEFAULT_SERVER_DATE_FORMAT) + right_date = (requested_date - security_delay).strftime( + DEFAULT_SERVER_DATETIME_FORMAT) + for line in so.order_line: + assert line.procurement_id, "No Procurement was created" + assert line.procurement_id.date_planned == right_date, "The planned date for the Procurement Order is wrong" + assert line.procurement_id.move_id.date_expected == right_date, "The expected date for the Stock Move is wrong" diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 26d9d80f13e..a675debd738 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -313,6 +313,15 @@ class sale_order(osv.osv): 'company_id': order.company_id.id, 'note': line.name, } + + def _order_line_move_date(self, cr, uid, line, context=None): + """Compute the Stock Move date for the Sale Order Line""" + date_planned = datetime.strptime(line.order_id.date_order, + DEFAULT_SERVER_DATE_FORMAT) + date_planned += timedelta(days=line.delay or 0.0) + date_planned -= timedelta(days=line.order_id.company_id.security_lead) + return date_planned.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + def _prepare_order_line_move(self, cr, uid, order, line, picking_id, date_planned, context=None): location_id = order.warehouse_id.lot_stock_id.id @@ -412,7 +421,7 @@ class sale_order(osv.osv): if line.state == 'done': continue - date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context) + date_planned = self._order_line_move_date(cr, uid, line, context=context) if line.product_id: if line.product_id.type in ('product', 'consu'):