diff --git a/lms/djangoapps/shoppingcart/migrations/0001_initial.py b/lms/djangoapps/shoppingcart/migrations/0001_initial.py index 779eccc94d..ea6a250f77 100644 --- a/lms/djangoapps/shoppingcart/migrations/0001_initial.py +++ b/lms/djangoapps/shoppingcart/migrations/0001_initial.py @@ -12,9 +12,20 @@ class Migration(SchemaMigration): db.create_table('shoppingcart_order', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('currency', self.gf('django.db.models.fields.CharField')(default='usd', max_length=8)), ('status', self.gf('django.db.models.fields.CharField')(default='cart', max_length=32)), - ('nonce', self.gf('django.db.models.fields.CharField')(max_length=128)), ('purchase_time', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('bill_to_first', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)), + ('bill_to_last', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)), + ('bill_to_street1', self.gf('django.db.models.fields.CharField')(max_length=128, blank=True)), + ('bill_to_street2', self.gf('django.db.models.fields.CharField')(max_length=128, blank=True)), + ('bill_to_city', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)), + ('bill_to_state', self.gf('django.db.models.fields.CharField')(max_length=8, blank=True)), + ('bill_to_postalcode', self.gf('django.db.models.fields.CharField')(max_length=16, blank=True)), + ('bill_to_country', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)), + ('bill_to_ccnum', self.gf('django.db.models.fields.CharField')(max_length=8, blank=True)), + ('bill_to_cardtype', self.gf('django.db.models.fields.CharField')(max_length=32, blank=True)), + ('processor_reply_dump', self.gf('django.db.models.fields.TextField')(blank=True)), )) db.send_create_signal('shoppingcart', ['Order']) @@ -25,9 +36,10 @@ class Migration(SchemaMigration): ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), ('status', self.gf('django.db.models.fields.CharField')(default='cart', max_length=32)), ('qty', self.gf('django.db.models.fields.IntegerField')(default=1)), - ('unit_cost', self.gf('django.db.models.fields.FloatField')(default=0.0)), - ('line_cost', self.gf('django.db.models.fields.FloatField')(default=0.0)), + ('unit_cost', self.gf('django.db.models.fields.DecimalField')(default=0.0, max_digits=30, decimal_places=2)), + ('line_cost', self.gf('django.db.models.fields.DecimalField')(default=0.0, max_digits=30, decimal_places=2)), ('line_desc', self.gf('django.db.models.fields.CharField')(default='Misc. Item', max_length=1024)), + ('currency', self.gf('django.db.models.fields.CharField')(default='usd', max_length=8)), )) db.send_create_signal('shoppingcart', ['OrderItem']) @@ -38,6 +50,15 @@ class Migration(SchemaMigration): )) db.send_create_signal('shoppingcart', ['PaidCourseRegistration']) + # Adding model 'CertificateItem' + db.create_table('shoppingcart_certificateitem', ( + ('orderitem_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['shoppingcart.OrderItem'], unique=True, primary_key=True)), + ('course_id', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('course_enrollment', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['student.CourseEnrollment'])), + ('mode', self.gf('django.db.models.fields.SlugField')(max_length=50)), + )) + db.send_create_signal('shoppingcart', ['CertificateItem']) + def backwards(self, orm): # Deleting model 'Order' @@ -49,6 +70,9 @@ class Migration(SchemaMigration): # Deleting model 'PaidCourseRegistration' db.delete_table('shoppingcart_paidcourseregistration') + # Deleting model 'CertificateItem' + db.delete_table('shoppingcart_certificateitem') + models = { 'auth.group': { @@ -87,29 +111,57 @@ class Migration(SchemaMigration): 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, + 'shoppingcart.certificateitem': { + 'Meta': {'object_name': 'CertificateItem', '_ormbases': ['shoppingcart.OrderItem']}, + 'course_enrollment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['student.CourseEnrollment']"}), + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'mode': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'}) + }, 'shoppingcart.order': { 'Meta': {'object_name': 'Order'}, + 'bill_to_cardtype': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'bill_to_ccnum': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}), + 'bill_to_city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'bill_to_country': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'bill_to_first': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'bill_to_last': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'bill_to_postalcode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}), + 'bill_to_state': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}), + 'bill_to_street1': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'bill_to_street2': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'nonce': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'processor_reply_dump': ('django.db.models.fields.TextField', [], {'blank': 'True'}), 'purchase_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) }, 'shoppingcart.orderitem': { 'Meta': {'object_name': 'OrderItem'}, + 'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'line_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), + 'line_cost': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'max_digits': '30', 'decimal_places': '2'}), 'line_desc': ('django.db.models.fields.CharField', [], {'default': "'Misc. Item'", 'max_length': '1024'}), 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}), 'qty': ('django.db.models.fields.IntegerField', [], {'default': '1'}), 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), - 'unit_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), + 'unit_cost': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'max_digits': '30', 'decimal_places': '2'}), 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) }, 'shoppingcart.paidcourseregistration': { 'Meta': {'object_name': 'PaidCourseRegistration', '_ormbases': ['shoppingcart.OrderItem']}, 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), 'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'student.courseenrollment': { + 'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'}, + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) } } diff --git a/lms/djangoapps/shoppingcart/migrations/0002_auto__del_field_order_nonce__add_field_order_bill_to_first__add_field_.py b/lms/djangoapps/shoppingcart/migrations/0002_auto__del_field_order_nonce__add_field_order_bill_to_first__add_field_.py deleted file mode 100644 index 940116f7b8..0000000000 --- a/lms/djangoapps/shoppingcart/migrations/0002_auto__del_field_order_nonce__add_field_order_bill_to_first__add_field_.py +++ /dev/null @@ -1,183 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Deleting field 'Order.nonce' - db.delete_column('shoppingcart_order', 'nonce') - - # Adding field 'Order.bill_to_first' - db.add_column('shoppingcart_order', 'bill_to_first', - self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_last' - db.add_column('shoppingcart_order', 'bill_to_last', - self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_street1' - db.add_column('shoppingcart_order', 'bill_to_street1', - self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_street2' - db.add_column('shoppingcart_order', 'bill_to_street2', - self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_city' - db.add_column('shoppingcart_order', 'bill_to_city', - self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_postalcode' - db.add_column('shoppingcart_order', 'bill_to_postalcode', - self.gf('django.db.models.fields.CharField')(max_length=16, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_country' - db.add_column('shoppingcart_order', 'bill_to_country', - self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_ccnum' - db.add_column('shoppingcart_order', 'bill_to_ccnum', - self.gf('django.db.models.fields.CharField')(max_length=8, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.bill_to_cardtype' - db.add_column('shoppingcart_order', 'bill_to_cardtype', - self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True), - keep_default=False) - - # Adding field 'Order.processor_reply_dump' - db.add_column('shoppingcart_order', 'processor_reply_dump', - self.gf('django.db.models.fields.TextField')(null=True, blank=True), - keep_default=False) - - # Adding field 'OrderItem.currency' - db.add_column('shoppingcart_orderitem', 'currency', - self.gf('django.db.models.fields.CharField')(default='usd', max_length=8), - keep_default=False) - - - def backwards(self, orm): - # Adding field 'Order.nonce' - db.add_column('shoppingcart_order', 'nonce', - self.gf('django.db.models.fields.CharField')(default='defaultNonce', max_length=128), - keep_default=False) - - # Deleting field 'Order.bill_to_first' - db.delete_column('shoppingcart_order', 'bill_to_first') - - # Deleting field 'Order.bill_to_last' - db.delete_column('shoppingcart_order', 'bill_to_last') - - # Deleting field 'Order.bill_to_street1' - db.delete_column('shoppingcart_order', 'bill_to_street1') - - # Deleting field 'Order.bill_to_street2' - db.delete_column('shoppingcart_order', 'bill_to_street2') - - # Deleting field 'Order.bill_to_city' - db.delete_column('shoppingcart_order', 'bill_to_city') - - # Deleting field 'Order.bill_to_postalcode' - db.delete_column('shoppingcart_order', 'bill_to_postalcode') - - # Deleting field 'Order.bill_to_country' - db.delete_column('shoppingcart_order', 'bill_to_country') - - # Deleting field 'Order.bill_to_ccnum' - db.delete_column('shoppingcart_order', 'bill_to_ccnum') - - # Deleting field 'Order.bill_to_cardtype' - db.delete_column('shoppingcart_order', 'bill_to_cardtype') - - # Deleting field 'Order.processor_reply_dump' - db.delete_column('shoppingcart_order', 'processor_reply_dump') - - # Deleting field 'OrderItem.currency' - db.delete_column('shoppingcart_orderitem', 'currency') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'shoppingcart.order': { - 'Meta': {'object_name': 'Order'}, - 'bill_to_cardtype': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), - 'bill_to_ccnum': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'bill_to_city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_country': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_first': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_last': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_postalcode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), - 'bill_to_street1': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), - 'bill_to_street2': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'processor_reply_dump': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'purchase_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'shoppingcart.orderitem': { - 'Meta': {'object_name': 'OrderItem'}, - 'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'line_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - 'line_desc': ('django.db.models.fields.CharField', [], {'default': "'Misc. Item'", 'max_length': '1024'}), - 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}), - 'qty': ('django.db.models.fields.IntegerField', [], {'default': '1'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), - 'unit_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'shoppingcart.paidcourseregistration': { - 'Meta': {'object_name': 'PaidCourseRegistration', '_ormbases': ['shoppingcart.OrderItem']}, - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), - 'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'}) - } - } - - complete_apps = ['shoppingcart'] \ No newline at end of file diff --git a/lms/djangoapps/shoppingcart/migrations/0003_auto__add_field_order_bill_to_state.py b/lms/djangoapps/shoppingcart/migrations/0003_auto__add_field_order_bill_to_state.py deleted file mode 100644 index 85923794b6..0000000000 --- a/lms/djangoapps/shoppingcart/migrations/0003_auto__add_field_order_bill_to_state.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding field 'Order.bill_to_state' - db.add_column('shoppingcart_order', 'bill_to_state', - self.gf('django.db.models.fields.CharField')(max_length=8, null=True, blank=True), - keep_default=False) - - - def backwards(self, orm): - # Deleting field 'Order.bill_to_state' - db.delete_column('shoppingcart_order', 'bill_to_state') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'shoppingcart.order': { - 'Meta': {'object_name': 'Order'}, - 'bill_to_cardtype': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), - 'bill_to_ccnum': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'bill_to_city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_country': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_first': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_last': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_postalcode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), - 'bill_to_state': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'bill_to_street1': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), - 'bill_to_street2': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'processor_reply_dump': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'purchase_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'shoppingcart.orderitem': { - 'Meta': {'object_name': 'OrderItem'}, - 'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'line_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - 'line_desc': ('django.db.models.fields.CharField', [], {'default': "'Misc. Item'", 'max_length': '1024'}), - 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}), - 'qty': ('django.db.models.fields.IntegerField', [], {'default': '1'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), - 'unit_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'shoppingcart.paidcourseregistration': { - 'Meta': {'object_name': 'PaidCourseRegistration', '_ormbases': ['shoppingcart.OrderItem']}, - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), - 'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'}) - } - } - - complete_apps = ['shoppingcart'] \ No newline at end of file diff --git a/lms/djangoapps/shoppingcart/migrations/0003_auto__add_verifiedcertificate.py b/lms/djangoapps/shoppingcart/migrations/0003_auto__add_verifiedcertificate.py deleted file mode 100644 index 25c0d46948..0000000000 --- a/lms/djangoapps/shoppingcart/migrations/0003_auto__add_verifiedcertificate.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'VerifiedCertificate' - db.create_table('shoppingcart_verifiedcertificate', ( - ('orderitem_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['shoppingcart.OrderItem'], unique=True, primary_key=True)), - ('course_id', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), - ('course_enrollment', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['student.CourseEnrollment'])), - )) - db.send_create_signal('shoppingcart', ['VerifiedCertificate']) - - - def backwards(self, orm): - # Deleting model 'VerifiedCertificate' - db.delete_table('shoppingcart_verifiedcertificate') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'shoppingcart.order': { - 'Meta': {'object_name': 'Order'}, - 'bill_to_cardtype': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), - 'bill_to_ccnum': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'bill_to_city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_country': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_first': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_last': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'bill_to_postalcode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), - 'bill_to_street1': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), - 'bill_to_street2': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'processor_reply_dump': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'purchase_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'shoppingcart.orderitem': { - 'Meta': {'object_name': 'OrderItem'}, - 'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'line_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - 'line_desc': ('django.db.models.fields.CharField', [], {'default': "'Misc. Item'", 'max_length': '1024'}), - 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}), - 'qty': ('django.db.models.fields.IntegerField', [], {'default': '1'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}), - 'unit_cost': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'shoppingcart.paidcourseregistration': { - 'Meta': {'object_name': 'PaidCourseRegistration', '_ormbases': ['shoppingcart.OrderItem']}, - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), - 'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'shoppingcart.verifiedcertificate': { - 'Meta': {'object_name': 'VerifiedCertificate', '_ormbases': ['shoppingcart.OrderItem']}, - 'course_enrollment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['student.CourseEnrollment']"}), - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), - 'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'student.courseenrollment': { - 'Meta': {'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'}, - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - } - } - - complete_apps = ['shoppingcart'] \ No newline at end of file diff --git a/lms/djangoapps/shoppingcart/models.py b/lms/djangoapps/shoppingcart/models.py index 54e7a33889..3a4039c9e1 100644 --- a/lms/djangoapps/shoppingcart/models.py +++ b/lms/djangoapps/shoppingcart/models.py @@ -1,21 +1,27 @@ import pytz import logging -from datetime import datetime +from datetime import datetime from django.db import models from django.core.exceptions import ObjectDoesNotExist from django.contrib.auth.models import User -from courseware.courses import course_image_url, get_course_about_section +from django.utils.translation import ugettext as _ +from model_utils.managers import InheritanceManager +from courseware.courses import get_course_about_section from student.views import course_from_id -from student.models import CourseEnrollmentAllowed, CourseEnrollment +from student.models import CourseEnrollment from statsd import statsd log = logging.getLogger("shoppingcart") +class InvalidCartItem(Exception): + pass + ORDER_STATUSES = ( ('cart', 'cart'), ('purchased', 'purchased'), ('refunded', 'refunded'), # Not used for now ) + class Order(models.Model): """ This is the model for an order. Before purchase, an Order and its related OrderItems are used @@ -23,43 +29,40 @@ class Order(models.Model): FOR ANY USER, THERE SHOULD ONLY EVER BE ZERO OR ONE ORDER WITH STATUS='cart'. """ user = models.ForeignKey(User, db_index=True) + currency = models.CharField(default="usd", max_length=8) # lower case ISO currency codes status = models.CharField(max_length=32, default='cart', choices=ORDER_STATUSES) purchase_time = models.DateTimeField(null=True, blank=True) # Now we store data needed to generate a reasonable receipt # These fields only make sense after the purchase - bill_to_first = models.CharField(max_length=64, null=True, blank=True) - bill_to_last = models.CharField(max_length=64, null=True, blank=True) - bill_to_street1 = models.CharField(max_length=128, null=True, blank=True) - bill_to_street2 = models.CharField(max_length=128, null=True, blank=True) - bill_to_city = models.CharField(max_length=64, null=True, blank=True) - bill_to_state = models.CharField(max_length=8, null=True, blank=True) - bill_to_postalcode = models.CharField(max_length=16, null=True, blank=True) - bill_to_country = models.CharField(max_length=64, null=True, blank=True) - bill_to_ccnum = models.CharField(max_length=8, null=True, blank=True) # last 4 digits - bill_to_cardtype = models.CharField(max_length=32, null=True, blank=True) + bill_to_first = models.CharField(max_length=64, blank=True) + bill_to_last = models.CharField(max_length=64, blank=True) + bill_to_street1 = models.CharField(max_length=128, blank=True) + bill_to_street2 = models.CharField(max_length=128, blank=True) + bill_to_city = models.CharField(max_length=64, blank=True) + bill_to_state = models.CharField(max_length=8, blank=True) + bill_to_postalcode = models.CharField(max_length=16, blank=True) + bill_to_country = models.CharField(max_length=64, blank=True) + bill_to_ccnum = models.CharField(max_length=8, blank=True) # last 4 digits + bill_to_cardtype = models.CharField(max_length=32, blank=True) # a JSON dump of the CC processor response, for completeness - processor_reply_dump = models.TextField(null=True, blank=True) + processor_reply_dump = models.TextField(blank=True) @classmethod def get_cart_for_user(cls, user): """ Always use this to preserve the property that at most 1 order per user has status = 'cart' """ - order, created = cls.objects.get_or_create(user=user, status='cart') - return order + # find the newest element in the db + try: + cart_order = cls.objects.filter(user=user, status='cart').order_by('-id')[:1].get() + except ObjectDoesNotExist: + # if nothing exists in the database, create a new cart + cart_order, _created = cls.objects.get_or_create(user=user, status='cart') + return cart_order @property def total_cost(self): - return sum([i.line_cost for i in self.orderitem_set.filter(status=self.status)]) - - @property - def currency(self): - """Assumes that all cart items are in the same currency""" - items = self.orderitem_set.all() - if not items: - return 'usd' - else: - return items[0].currency + return sum(i.line_cost for i in self.orderitem_set.filter(status=self.status)) def clear(self): """ @@ -87,7 +90,10 @@ class Order(models.Model): self.bill_to_cardtype = cardtype self.processor_reply_dump = processor_reply_dump self.save() - for item in self.orderitem_set.all(): + # this should return all of the objects with the correct types of the + # subclasses + orderitems = OrderItem.objects.filter(order=self).select_subclasses() + for item in orderitems: item.status = 'purchased' item.purchased_callback() item.save() @@ -101,60 +107,38 @@ class OrderItem(models.Model): Each implementation of OrderItem should provide its own purchased_callback as a method. """ + objects = InheritanceManager() order = models.ForeignKey(Order, db_index=True) # this is denormalized, but convenient for SQL queries for reports, etc. user should always be = order.user user = models.ForeignKey(User, db_index=True) # this is denormalized, but convenient for SQL queries for reports, etc. status should always be = order.status status = models.CharField(max_length=32, default='cart', choices=ORDER_STATUSES) qty = models.IntegerField(default=1) - unit_cost = models.FloatField(default=0.0) - line_cost = models.FloatField(default=0.0) # qty * unit_cost + unit_cost = models.DecimalField(default=0.0, decimal_places=2, max_digits=30) + line_cost = models.DecimalField(default=0.0, decimal_places=2, max_digits=30) # qty * unit_cost line_desc = models.CharField(default="Misc. Item", max_length=1024) - currency = models.CharField(default="usd", max_length=8) # lower case ISO currency codes + currency = models.CharField(default="usd", max_length=8) # lower case ISO currency codes - def add_to_order(self, *args, **kwargs): + @classmethod + def add_to_order(cls, *args, **kwargs): """ A suggested convenience function for subclasses. """ - raise NotImplementedError + # this is a validation step to verify that the currency of the item we + # are adding is the same as the currency of the order we are adding it + # to + if isinstance(args[0], Order): + currency = kwargs['currency'] if 'currency' in kwargs else 'usd' + order = args[0] + if order.currency != currency and order.orderitem_set.count() > 0: + raise InvalidCartItem(_("Trying to add a different currency into the cart")) def purchased_callback(self): """ This is called on each inventory item in the shopping cart when the purchase goes through. - - NOTE: We want to provide facilities for doing something like - for item in OrderItem.objects.filter(order_id=order_id): - item.purchased_callback() - - Unfortunately the QuerySet used determines the class to be OrderItem, and not its most specific - subclasses. That means this parent class implementation of purchased_callback needs to act as - a dispatcher to call the callback the proper subclasses, and as such it needs to know about all - possible subclasses. - So keep ORDER_ITEM_SUBTYPES up-to-date """ - for cls, lc_classname in ORDER_ITEM_SUBTYPES.iteritems(): - try: - #Uses https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance to test subclass - sub_instance = getattr(self,lc_classname) - sub_instance.purchased_callback() - except (ObjectDoesNotExist, AttributeError): - log.exception('Cannot call purchase_callback on non-existent subclass attribute {0} of OrderItem'\ - .format(lc_classname)) - pass - - def is_of_subtype(self, cls): - """ - Checks if self is also a type of cls, in addition to being an OrderItem - Uses https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance to test for subclass - """ - if cls not in ORDER_ITEM_SUBTYPES: - return False - try: - getattr(self, ORDER_ITEM_SUBTYPES[cls]) - return True - except (ObjectDoesNotExist, AttributeError): - return False + raise NotImplementedError class PaidCourseRegistration(OrderItem): @@ -180,6 +164,8 @@ class PaidCourseRegistration(OrderItem): Returns the order item """ + super(PaidCourseRegistration, cls).add_to_order(order, course_id, cost, currency=currency) + # TODO: Possibly add checking for whether student is already enrolled in course course = course_from_id(course_id) # actually fetch the course to make sure it exists, use this to # throw errors if it doesn't @@ -190,6 +176,8 @@ class PaidCourseRegistration(OrderItem): item.line_cost = cost item.line_desc = 'Registration for Course: {0}'.format(get_course_about_section(course, "title")) item.currency = currency + order.currency = currency + order.save() item.save() return item @@ -214,45 +202,61 @@ class PaidCourseRegistration(OrderItem): "run:{0}".format(run)]) -class VerifiedCertificate(OrderItem): +class CertificateItem(OrderItem): """ - This is an inventory item for purchasing verified certificates + This is an inventory item for purchasing certificates """ course_id = models.CharField(max_length=128, db_index=True) course_enrollment = models.ForeignKey(CourseEnrollment) + mode = models.SlugField() @classmethod - def add_to_order(cls, order, course_id, cost, currency='usd'): + def add_to_order(cls, order, course_id, cost, mode, currency='usd'): """ - Add a VerifiedCertificate item to an order + Add a CertificateItem to an order + + Returns the CertificateItem object after saving + + `order` - an order that this item should be added to, generally the cart order + `course_id` - the course that we would like to purchase as a CertificateItem + `cost` - the amount the user will be paying for this CertificateItem + `mode` - the course mode that this certificate is going to be issued for + + This item also creates a new enrollment if none exists for this user and this course. + + Example Usage: + cart = Order.get_cart_for_user(user) + CertificateItem.add_to_order(cart, 'edX/Test101/2013_Fall', 30, 'verified') + """ - course_enrollment = CourseEnrollment.create_enrollment(order.user, course_id, mode="verified") + super(CertificateItem, cls).add_to_order(order, course_id, cost, currency=currency) + try: + course_enrollment = CourseEnrollment.objects.get(user=order.user, course_id=course_id) + except ObjectDoesNotExist: + course_enrollment = CourseEnrollment.create_enrollment(order.user, course_id, mode=mode) item, _created = cls.objects.get_or_create( order=order, user=order.user, course_id=course_id, - course_enrollment=course_enrollment + course_enrollment=course_enrollment, + mode=mode ) item.status = order.status item.qty = 1 item.unit_cost = cost item.line_cost = cost - item.line_desc = "Verified Certificate for Course {0}".format(course_id) + item.line_desc = "{mode} certificate for course {course_id}".format(mode=item.mode, + course_id=course_id) item.currency = currency + order.currency = currency + order.save() item.save() return item def purchased_callback(self): """ - When purchase goes through, activate the course enrollment + When purchase goes through, activate and update the course enrollment for the correct mode """ + self.course_enrollment.mode = self.mode + self.course_enrollment.save() self.course_enrollment.activate() - - -# Each entry is a dictionary of ModelName: 'lower_case_model_name' -# See https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance for -# PLEASE KEEP THIS LIST UP_TO_DATE WITH THE SUBCLASSES OF OrderItem -ORDER_ITEM_SUBTYPES = { - PaidCourseRegistration: 'paidcourseregistration', - VerifiedCertificate: 'verifiedcertificate', -} diff --git a/lms/djangoapps/shoppingcart/tests.py b/lms/djangoapps/shoppingcart/tests.py index 521b9e594e..61a10f2f75 100644 --- a/lms/djangoapps/shoppingcart/tests.py +++ b/lms/djangoapps/shoppingcart/tests.py @@ -4,32 +4,86 @@ Tests for the Shopping Cart from factory import DjangoModelFactory from django.test import TestCase -from shoppingcart.models import Order, VerifiedCertificate +from shoppingcart.models import Order, CertificateItem, InvalidCartItem from student.tests.factories import UserFactory - - -class OrderFactory(DjangoModelFactory): - FACTORY_FOR = Order - - -class VerifiedCertificateFactory(DjangoModelFactory): - FACTORY_FOR = VerifiedCertificate +from student.models import CourseEnrollment class OrderTest(TestCase): def setUp(self): self.user = UserFactory.create() - self.cart = OrderFactory.create(user=self.user, status='cart') self.course_id = "test/course" + self.cost = 40 - def test_add_item_to_cart(self): - pass + def test_get_cart_for_user(self): + # create a cart + cart = Order.get_cart_for_user(user=self.user) + # add something to it + CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified') + # should return the same cart + cart2 = Order.get_cart_for_user(user=self.user) + self.assertEquals(cart2.orderitem_set.count(), 1) + + def test_cart_clear(self): + cart = Order.get_cart_for_user(user=self.user) + CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified') + CertificateItem.add_to_order(cart, 'test/course1', self.cost, 'verified') + self.assertEquals(cart.orderitem_set.count(), 2) + cart.clear() + self.assertEquals(cart.orderitem_set.count(), 0) + + def test_add_item_to_cart_currency_match(self): + cart = Order.get_cart_for_user(user=self.user) + CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified', currency='eur') + # verify that a new item has been added + self.assertEquals(cart.orderitem_set.count(), 1) + # verify that the cart's currency was updated + self.assertEquals(cart.currency, 'eur') + with self.assertRaises(InvalidCartItem): + CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified', currency='usd') + # assert that this item did not get added to the cart + self.assertEquals(cart.orderitem_set.count(), 1) def test_total_cost(self): + cart = Order.get_cart_for_user(user=self.user) # add items to the order - cost = 30 - iterations = 5 - for _ in xrange(iterations): - VerifiedCertificate.add_to_order(self.cart, self.user, self.course_id, cost) - self.assertEquals(self.cart.total_cost, cost * iterations) + course_costs = [('test/course1', 30), + ('test/course2', 40), + ('test/course3', 10), + ('test/course4', 20)] + for course, cost in course_costs: + CertificateItem.add_to_order(cart, course, cost, 'verified') + self.assertEquals(cart.orderitem_set.count(), len(course_costs)) + self.assertEquals(cart.total_cost, sum(cost for _course, cost in course_costs)) + def test_purchase(self): + # This test is for testing the subclassing functionality of OrderItem, but in + # order to do this, we end up testing the specific functionality of + # CertificateItem, which is not quite good unit test form. Sorry. + cart = Order.get_cart_for_user(user=self.user) + self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id)) + CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified') + # course enrollment object should be created but still inactive + self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id)) + cart.purchase() + self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id)) + + +class CertificateItemTest(TestCase): + """ + Tests for verifying specific CertificateItem functionality + """ + def setUp(self): + self.user = UserFactory.create() + self.course_id = "test/course" + self.cost = 40 + + def test_existing_enrollment(self): + CourseEnrollment.enroll(self.user, self.course_id) + cart = Order.get_cart_for_user(user=self.user) + CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified') + # verify that we are still enrolled + self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id)) + cart.purchase() + enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id) + self.assertEquals(enrollment.mode, u'verified') diff --git a/lms/djangoapps/shoppingcart/views.py b/lms/djangoapps/shoppingcart/views.py index 91dff59aed..bdf8eb317f 100644 --- a/lms/djangoapps/shoppingcart/views.py +++ b/lms/djangoapps/shoppingcart/views.py @@ -38,7 +38,7 @@ def add_course_to_cart(request, course_id): @login_required def register_for_verified_cert(request, course_id): cart = Order.get_cart_for_user(request.user) - VerifiedCertificate.add_to_order(cart, course_id, 30) + CertificateItem.add_to_order(cart, course_id, 30, 'verified') return HttpResponse("Added") @login_required diff --git a/lms/envs/common.py b/lms/envs/common.py index c5b174b077..8181f97789 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -438,10 +438,10 @@ ZENDESK_API_KEY = None PAYMENT_SUPPORT_EMAIL = 'payment@edx.org' ##### Using cybersource by default ##### CC_PROCESSOR = { - 'CyberSource' : { + 'CyberSource': { 'SHARED_SECRET': '', - 'MERCHANT_ID' : '', - 'SERIAL_NUMBER' : '', + 'MERCHANT_ID': '', + 'SERIAL_NUMBER': '', 'ORDERPAGE_VERSION': '7', 'PURCHASE_ENDPOINT': '', } diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 9179315797..d700aaa195 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -53,6 +53,7 @@ South==0.7.6 sympy==0.7.1 xmltodict==0.4.1 django-ratelimit-backend==0.6 +django-model-utils==1.4.0 # Used for debugging ipython==0.13.1