Index: main/waeup.ikoba/trunk/src/waeup/ikoba/browser/templates/datacenterexportpage.pt
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/browser/templates/datacenterexportpage.pt	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/browser/templates/datacenterexportpage.pt	(revision 12633)
@@ -29,4 +29,5 @@
       name="CREATE" tal:attributes="value view/export_button" />
   </div>
+  <br /><br />
   <div class="form-group">
   <input type="submit" name="CANCEL" class="btn btn-default"
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/batching.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/batching.py	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/batching.py	(revision 12633)
@@ -31,4 +31,5 @@
 from zope.interface import Interface
 from zope.schema import getFields
+from zope.formlib.interfaces import ConversionError
 from zope.component import queryUtility, getUtility, createObject
 from zope.event import notify
@@ -38,5 +39,5 @@
     IBatchProcessor, FatalCSVError, IObjectConverter, IUserAccount,
     IObjectHistory, IGNORE_MARKER)
-from waeup.ikoba.interfaces import IIkobaUtils
+from waeup.ikoba.interfaces import IIkobaUtils, DELETION_MARKER, IGNORE_MARKER
 from waeup.ikoba.interfaces import MessageFactory as _
 from waeup.ikoba.customers.utils import generate_contract_id
@@ -47,4 +48,18 @@
     IMPORTABLE_REGISTRATION_STATES, IMPORTABLE_REGISTRATION_TRANSITIONS)
 from waeup.ikoba.utils.batching import BatchProcessor
+
+DATE_FORMAT = '%Y-%m-%d'
+
+
+def date_to_field(input):
+    # In import files we can use the hash symbol at the end of a
+    # date string to avoid annoying automatic date transformation
+    # by Excel or Calc
+    input = input.strip('#')
+    try:
+        value = datetime.strptime(input, DATE_FORMAT)
+    except (ValueError, IndexError), v:
+        raise ConversionError("Invalid datetime data", v)
+    return value.date()
 
 
@@ -481,5 +496,6 @@
 
     location_fields = []
-    additional_fields = ['class_name']
+    additional_fields = ['class_name', 'title',
+                         'tc_dict', 'valid_from', 'valid_to']
     additional_headers = ['class_name']
 
@@ -551,4 +567,42 @@
             if class_name != self.factory_name.strip('waeup.'):
                 errs.append(('class_name','wrong processor'))
+        tc_dict = row.get('tc_dict', None)
+        # We need to check tc_dict, valid_to and valid_from because they
+        # are not schema fields are thus not checked by the
+        # DefaultObjectConverter.
+        while True:
+            if tc_dict in (IGNORE_MARKER, None, {}, ''):
+                break
+            if tc_dict == DELETION_MARKER:
+                conv_dict['tc_dict'] = None
+                break
+            try:
+                tc_dict = eval(tc_dict)
+                conv_dict['tc_dict'] = tc_dict
+                break
+            except SyntaxError:
+                errs.append(('tc_dict','syntax error'))
+                break
+            if not isinstance(tc_dict, dict):
+                errs.append(('tc_dict','not a dictionary'))
+                break
+        valid_from = row.get('valid_from', None)
+        while True:
+            if valid_from in (IGNORE_MARKER, None, ''):
+                break
+            try:
+                date = date_to_field(valid_from)
+            except ConversionError:
+                errs.append(('valid_from','invalid date format'))
+            break
+        valid_to = row.get('valid_to', None)
+        while True:
+            if valid_to in (IGNORE_MARKER, None, ''):
+                break
+            try:
+                date = date_to_field(valid_to)
+            except ConversionError:
+                errs.append(('valid_to','invalid date format'))
+            break
         return errs, inv_errs, conv_dict
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py	(revision 12633)
@@ -1271,7 +1271,4 @@
     pnav = 4
 
-    form_fields = grok.AutoFields(IContract).omit(
-        'product_object', 'contract_id', 'product_options')
-
     @property
     def edit_contracts_allowed(self):
@@ -1301,5 +1298,4 @@
         # classes depending on the contype parameter given in form.
         contract = createObject('waeup.%s' % contype)
-        self.applyData(contract, **data)
         self.context.addContract(contract)
         contype = getUtility(ICustomersUtils).SELECTABLE_CONTYPES_DICT[contype]
@@ -1339,4 +1335,6 @@
         self.context.title = self.context.product_object.contract_autotitle
         self.context.tc_dict = self.context.product_object.tc_dict
+        self.context.valid_from = self.context.product_object.valid_from
+        self.context.valid_to = self.context.product_object.valid_to
         isCustomer = getattr(
             self.request.principal, 'user_type', None) == 'customer'
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/contractaddpage.pt
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/contractaddpage.pt	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/contractaddpage.pt	(revision 12633)
@@ -17,5 +17,4 @@
         </td>
       </tr>
-      <tal:widgets content="structure provider:widgets" />
     </tbody>
   </table>
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/contracts.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/contracts.py	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/contracts.py	(revision 12633)
@@ -76,4 +76,6 @@
         self.tc_dict = {}
         self.title = None
+        self.valid_to = None
+        self.valid_from = None
         return
 
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py	(revision 12633)
@@ -301,4 +301,6 @@
     title = Attribute('Title generated by the associated product')
     tc_dict = Attribute('Multilingual "Terms and Conditions" dict')
+    valid_from = Attribute('Contract valid from')
+    valid_to = Attribute('Contract valid to')
 
     contract_id = schema.TextLine(
@@ -364,6 +366,14 @@
     """
 
+    #title = schema.TextLine(
+    #    required = False,
+    #    )
+
+    #tc_dict = schema.Dict(
+    #    required = False,
+    #    default = {},
+    #    )
+
     product_options = schema.List(
-        title = _(u'Options/Fees'),
         value_type = ProductOptionField(),
         required = False,
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/sample_contract_data.csv
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/sample_contract_data.csv	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/sample_contract_data.csv	(revision 12633)
@@ -1,8 +1,8 @@
-class_name,contract_id,reg_number,product_object,document_object,product_options,tc_dict,title
-SampleContract,c3,1,SAM,DOC1,"[('Base Fee', '800.6', 'USD')]",{'en':'Hello World'},Nice Contract
-SampleContract,c4,2,SAM,DOC1,[],{},
-SampleContract,c5,3,SAM,DOC1,[],{},
-SampleContract,,1,SAM,DOC1,[],{},
-SampleContract,c5,2,SAM,DOC1,[],{},
-SampleContract,c6,3,SAM,NONEXISTENT,[],{},
-SampleContract,c7,3,NONEXISTENT,DOC1,[],{},
+class_name,contract_id,reg_number,product_object,document_object,product_options,tc_dict,title,valid_from,valid_to
+SampleContract,c3,1,SAM,DOC1,"[('Base Fee', '800.6', 'USD')]",{'en':'Hello World'},Nice Contract,2014-06-25#,2015-06-25#
+SampleContract,c4,2,SAM,DOC1,[],,,,
+SampleContract,c5,3,SAM,DOC1,[],,,,
+SampleContract,,1,SAM,DOC1,[],,,,
+SampleContract,c5,2,SAM,DOC1,[],,,,
+SampleContract,c6,3,SAM,NONEXISTENT,[],,,,
+SampleContract,c7,3,NONEXISTENT,DOC1,[],,,,
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_batching.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_batching.py	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_batching.py	(revision 12633)
@@ -158,4 +158,7 @@
         contract.product_options = self.product.options
         contract.tc_dict = {'en': u'Hello world'}
+        contract.valid_from = datetime.date(2014, 2, 4)
+        contract.valid_to = datetime.date(2014, 12, 4)
+
         customer['contracts'].addContract(contract)
         self.contract = contract
@@ -498,8 +501,8 @@
         fail_file = open(fail_file).read()
         self.assertEqual(fail_file,
-            'reg_number,contract_id,title,class_name,product_object,tc_dict,document_object,product_options,--ERRORS--\r\n'
-            '2,c5,,SampleContract,SAM,{},DOC1,[],This object already exists. Skipping.\r\n'
-            '3,c6,,SampleContract,SAM,{},NONEXISTENT,[],document_object: Invalid value\r\n'
-            '3,c7,,SampleContract,NONEXISTENT,{},DOC1,[],product_object: Invalid value\r\n'
+            'reg_number,valid_from,contract_id,title,class_name,valid_to,product_object,tc_dict,document_object,product_options,--ERRORS--\r\n'
+            '2,,c5,,SampleContract,,SAM,,DOC1,[],This object already exists. Skipping.\r\n'
+            '3,,c6,,SampleContract,,SAM,,NONEXISTENT,[],document_object: Invalid value\r\n'
+            '3,,c7,,SampleContract,,NONEXISTENT,,DOC1,[],product_object: Invalid value\r\n'
             )
         contract = self.processor.getEntry(dict(reg_number='1',
@@ -512,5 +515,6 @@
         self.assertEqual(contract.product_options[0].currency, 'USD')
         self.assertEqual(contract.contract_id, 'c3')
-        self.assertEqual(contract.tc_dict, u"{'en':'Hello World'}")
+        self.assertEqual(contract.title, u'Nice Contract')
+        self.assertEqual(contract.tc_dict, {'en':'Hello World'})
         contract = self.processor.getEntry(dict(reg_number='3',
             contract_id='c5'), self.app)
@@ -523,5 +527,5 @@
         self.assertTrue(
             'INFO - system - Customer Sample Contract Processor - sample_contract_data - '
-            'X666666 - %s - updated: title=, product_object=SAM, tc_dict={}, document_object=DOC1, '
+            'X666666 - %s - updated: valid_from=, title=, valid_to=, product_object=SAM, tc_dict=, document_object=DOC1, '
             'product_options=[]' % conid
             in logcontent)
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_export.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_export.py	(revision 12632)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_export.py	(revision 12633)
@@ -257,7 +257,8 @@
             'class_name,comment,contract_category,contract_id,document_object,'
             'history,last_product_id,'
-            'product_object,product_options,state,tc_dict,title,user_id\r\n'
-
-            'SampleContract,,sample,CON1,,[],,,[],,{},,\r\n'
+            'product_object,product_options,state,tc_dict,title,user_id,'
+            'valid_from,valid_to\r\n'
+
+            'SampleContract,,sample,CON1,,[],,,[],,{},,,,\r\n'
             )
         return
@@ -273,10 +274,12 @@
             'class_name,comment,contract_category,contract_id,document_object,'
             'history,last_product_id,'
-            'product_object,product_options,state,tc_dict,title,user_id\r\n'
+            'product_object,product_options,state,tc_dict,title,user_id,'
+            'valid_from,valid_to\r\n'
 
             'SampleContract,,sample,CON1,DOC1,[u\'2014-12-04 12:10:46 UTC - '
             'Contract created by system\'],,'
             'SAM,"[(u\'Base Fee\', u\'800.6\', u\'USD\')]",'
-            'created,{\'en\': u\'Hello world\'},,A111111\r\n',
+            'created,{\'en\': u\'Hello world\'},,A111111,'
+            '2014-02-04#,2014-12-04#\r\n',
             result
             )
@@ -293,10 +296,12 @@
             'class_name,comment,contract_category,contract_id,document_object,'
             'history,last_product_id,'
-            'product_object,product_options,state,tc_dict,title,user_id\r\n'
+            'product_object,product_options,state,tc_dict,title,user_id,'
+            'valid_from,valid_to\r\n'
 
             'SampleContract,,sample,CON1,DOC1,[u\'2014-12-04 12:10:46 UTC - '
             'Contract created by system\'],,'
             'SAM,"[(u\'Base Fee\', u\'800.6\', u\'USD\')]",'
-            'created,{\'en\': u\'Hello world\'},,A111111\r\n',
+            'created,{\'en\': u\'Hello world\'},,A111111,'
+            '2014-02-04#,2014-12-04#\r\n',
             result
             )
@@ -313,12 +318,14 @@
             'class_name,comment,contract_category,contract_id,document_object,'
             'history,last_product_id,'
-            'product_object,product_options,state,tc_dict,title,user_id\r\n'
+            'product_object,product_options,state,tc_dict,title,user_id,'
+            'valid_from,valid_to\r\n'
 
             'SampleContract,,sample,CON1,DOC1,[u\'2014-12-04 12:10:46 UTC - '
             'Contract created by system\'],,'
             'SAM,"[(u\'Base Fee\', u\'800.6\', u\'USD\')]",'
-            'created,{\'en\': u\'Hello world\'},,A111111\r\n',
-            result
-            )
-        return
-
+            'created,{\'en\': u\'Hello world\'},,A111111,'
+            '2014-02-04#,2014-12-04#\r\n',
+            result
+            )
+        return
+
