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..b34c1143ec0 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 are 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..f3ff971be9f 100644 --- a/addons/sale_order_dates/sale_order_dates.py +++ b/addons/sale_order_dates/sale_order_dates.py @@ -19,15 +19,37 @@ # ############################################################################## -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 Sales Order""" + default = dict(default or {}, requested_date=False) + return super(sale_order_dates, self).copy(cr, uid, id, default=default, + context=context) + + def _get_date_planned(self, cr, uid, order, line, start_date, context=None): + """Compute the expected date from the requested date, not the order date""" + if order and order.requested_date: + planned_str = self.date_to_datetime(cr, uid, + order.requested_date, context) + date_planned = datetime.strptime(planned_str, + DEFAULT_SERVER_DATETIME_FORMAT) + date_planned -= timedelta(days=order.company_id.security_lead) + return date_planned.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + return super(sale_order_dates, self)._get_date_planned( + cr, uid, order, line, start_date, context=context) + def _get_effective_date(self, cr, uid, ids, name, arg, context=None): + """Read the shipping date from the related packings""" + # TODO: 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 +63,57 @@ 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): + order_datetime_str = self.date_to_datetime(cr, uid, order.date_order, + context) + order_datetime = datetime.strptime(order_datetime_str, + DEFAULT_SERVER_DATETIME_FORMAT) 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 = order_datetime + 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.") + } + } + 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 are 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..cc216ddd9b9 100644 --- a/addons/sale_order_dates/sale_order_dates_view.xml +++ b/addons/sale_order_dates/sale_order_dates_view.xml @@ -10,13 +10,23 @@ - + + + sale.order + + + + + + + + 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..d05191a4d99 --- /dev/null +++ b/addons/sale_order_dates/test/requested_date.yml @@ -0,0 +1,32 @@ +- + 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}: | + self.write(cr, uid, ref("sale.sale_order_6"), {'requested_date': '2010-07-12'}) +- + I confirm the Sale Order. +- + !workflow { + model: sale.order, action: order_confirm, + ref: sale.sale_order_6 + } +- + 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 openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT + + so = self.browse(cr, uid, ref("sale.sale_order_6")) + security_delay = timedelta(days=so.company_id.security_lead) + requested_date = datetime.strptime( + self.date_to_datetime(cr, uid, so.requested_date), + DEFAULT_SERVER_DATETIME_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..f90aecac184 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -21,7 +21,6 @@ ############################################################################## from datetime import datetime, timedelta from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP, float_compare -from dateutil.relativedelta import relativedelta from openerp.osv import fields, osv from openerp.tools.translate import _ import pytz @@ -257,7 +256,7 @@ class sale_order(osv.osv): if tz_name: utc = pytz.timezone('UTC') context_tz = pytz.timezone(tz_name) - user_datetime = user_date + relativedelta(hours=12.0) + user_datetime = user_date + timedelta(hours=12.0) local_timestamp = context_tz.localize(user_datetime, is_dst=False) user_datetime = local_timestamp.astimezone(utc) return user_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) @@ -379,8 +378,9 @@ class sale_order(osv.osv): return True def _get_date_planned(self, cr, uid, order, line, start_date, context=None): + """Compute the Stock Move date for the Sale Order Line""" 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 = datetime.strptime(start_date, DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(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