Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
847f56d1
Commit
847f56d1
authored
Feb 09, 2015
by
Will Daly
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6927 from edx/will/invoice-transaction-history
Add InvoiceHistory model to record changes to invoices
parents
268280df
8fba97ea
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
589 additions
and
4 deletions
+589
-4
lms/djangoapps/shoppingcart/migrations/0027_add_invoice_history.py
+262
-0
lms/djangoapps/shoppingcart/models.py
+161
-0
lms/djangoapps/shoppingcart/tests/test_models.py
+166
-4
No files found.
lms/djangoapps/shoppingcart/migrations/0027_add_invoice_history.py
0 → 100644
View file @
847f56d1
# -*- 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 'InvoiceHistory'
db
.
create_table
(
'shoppingcart_invoicehistory'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'timestamp'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
auto_now_add
=
True
,
db_index
=
True
,
blank
=
True
)),
(
'invoice'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'shoppingcart.Invoice'
])),
(
'snapshot'
,
self
.
gf
(
'django.db.models.fields.TextField'
)(
blank
=
True
)),
))
db
.
send_create_signal
(
'shoppingcart'
,
[
'InvoiceHistory'
])
def
backwards
(
self
,
orm
):
# Deleting model 'InvoiceHistory'
db
.
delete_table
(
'shoppingcart_invoicehistory'
)
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.certificateitem'
:
{
'Meta'
:
{
'object_name'
:
'CertificateItem'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_enrollment'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['student.CourseEnrollment']"
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'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.coupon'
:
{
'Meta'
:
{
'object_name'
:
'Coupon'
},
'code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
}),
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime(2015, 2, 8, 0, 0)'
}),
'created_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'description'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'expiration_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'percentage_discount'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'0'
})
},
'shoppingcart.couponredemption'
:
{
'Meta'
:
{
'object_name'
:
'CouponRedemption'
},
'coupon'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Coupon']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.courseregcodeitem'
:
{
'Meta'
:
{
'object_name'
:
'CourseRegCodeItem'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'default'
:
"'honor'"
,
'max_length'
:
'50'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.courseregcodeitemannotation'
:
{
'Meta'
:
{
'object_name'
:
'CourseRegCodeItemAnnotation'
},
'annotation'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'shoppingcart.courseregistrationcode'
:
{
'Meta'
:
{
'object_name'
:
'CourseRegistrationCode'
},
'code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime(2015, 2, 8, 0, 0)'
}),
'created_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'created_by_user'"
,
'to'
:
"orm['auth.User']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'invoice'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Invoice']"
,
'null'
:
'True'
}),
'invoice_item'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.CourseRegistrationCodeInvoiceItem']"
,
'null'
:
'True'
}),
'mode_slug'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
,
'null'
:
'True'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'purchase_order'"
,
'null'
:
'True'
,
'to'
:
"orm['shoppingcart.Order']"
})
},
'shoppingcart.courseregistrationcodeinvoiceitem'
:
{
'Meta'
:
{
'object_name'
:
'CourseRegistrationCodeInvoiceItem'
,
'_ormbases'
:
[
'shoppingcart.InvoiceItem'
]},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'invoiceitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.InvoiceItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.donation'
:
{
'Meta'
:
{
'object_name'
:
'Donation'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'donation_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'general'"
,
'max_length'
:
'32'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.donationconfiguration'
:
{
'Meta'
:
{
'object_name'
:
'DonationConfiguration'
},
'change_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'changed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
,
'null'
:
'True'
,
'on_delete'
:
'models.PROTECT'
}),
'enabled'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'shoppingcart.invoice'
:
{
'Meta'
:
{
'object_name'
:
'Invoice'
},
'address_line_1'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'address_line_2'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'address_line_3'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'city'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
}),
'company_contact_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'company_contact_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'company_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'country'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'customer_reference_number'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'63'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'internal_reference'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'is_valid'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'recipient_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'recipient_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'state'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
}),
'total_amount'
:
(
'django.db.models.fields.FloatField'
,
[],
{}),
'zip'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'15'
,
'null'
:
'True'
})
},
'shoppingcart.invoicehistory'
:
{
'Meta'
:
{
'object_name'
:
'InvoiceHistory'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'invoice'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Invoice']"
}),
'snapshot'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'timestamp'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
})
},
'shoppingcart.invoiceitem'
:
{
'Meta'
:
{
'object_name'
:
'InvoiceItem'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'invoice'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Invoice']"
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'qty'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'1'
}),
'unit_price'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'default'
:
'0.0'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
})
},
'shoppingcart.invoicetransaction'
:
{
'Meta'
:
{
'object_name'
:
'InvoiceTransaction'
},
'amount'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'default'
:
'0.0'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
}),
'comments'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'created_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'invoice'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Invoice']"
}),
'last_modified_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'last_modified_by_user'"
,
'to'
:
"orm['auth.User']"
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'started'"
,
'max_length'
:
'32'
})
},
'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'
}),
'company_contact_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'company_contact_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'company_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'customer_reference_number'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'63'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'order_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'personal'"
,
'max_length'
:
'32'
}),
'processor_reply_dump'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'purchase_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'recipient_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'recipient_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'refunded_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'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'fulfilled_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'line_desc'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'Misc. Item'"
,
'max_length'
:
'1024'
}),
'list_price'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'null'
:
'True'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
}),
'qty'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'1'
}),
'refund_requested_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'report_comments'
:
(
'django.db.models.fields.TextField'
,
[],
{
'default'
:
"''"
}),
'service_fee'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'default'
:
'0.0'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'cart'"
,
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'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_enrollment'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['student.CourseEnrollment']"
,
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'default'
:
"'honor'"
,
'max_length'
:
'50'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'shoppingcart.paidcourseregistrationannotation'
:
{
'Meta'
:
{
'object_name'
:
'PaidCourseRegistrationAnnotation'
},
'annotation'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'shoppingcart.registrationcoderedemption'
:
{
'Meta'
:
{
'object_name'
:
'RegistrationCodeRedemption'
},
'course_enrollment'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['student.CourseEnrollment']"
,
'null'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
,
'null'
:
'True'
}),
'redeemed_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime(2015, 2, 8, 0, 0)'
,
'null'
:
'True'
}),
'redeemed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'registration_code'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.CourseRegistrationCode']"
})
},
'student.courseenrollment'
:
{
'Meta'
:
{
'ordering'
:
"('user', 'course_id')"
,
'unique_together'
:
"(('user', 'course_id'),)"
,
'object_name'
:
'CourseEnrollment'
},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'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']"
})
}
}
complete_apps
=
[
'shoppingcart'
]
lms/djangoapps/shoppingcart/models.py
View file @
847f56d1
...
...
@@ -4,6 +4,7 @@ from collections import namedtuple
from
datetime
import
datetime
from
datetime
import
timedelta
from
decimal
import
Decimal
import
json
import
analytics
from
io
import
BytesIO
import
pytz
...
...
@@ -21,6 +22,8 @@ from django.contrib.auth.models import User
from
django.utils.translation
import
ugettext
as
_
,
ugettext_lazy
from
django.db
import
transaction
from
django.db.models
import
Sum
from
django.db.models.signals
import
post_save
,
post_delete
from
django.core.urlresolvers
import
reverse
from
model_utils.managers
import
InheritanceManager
from
model_utils.models
import
TimeStampedModel
...
...
@@ -854,6 +857,48 @@ class Invoice(TimeStampedModel):
return
pdf_buffer
def
snapshot
(
self
):
"""Create a snapshot of the invoice.
A snapshot is a JSON-serializable representation
of the invoice's state, including its line items
and associated transactions (payments/refunds).
This is useful for saving the history of changes
to the invoice.
Returns:
dict
"""
return
{
'internal_reference'
:
self
.
internal_reference
,
'customer_reference'
:
self
.
customer_reference_number
,
'is_valid'
:
self
.
is_valid
,
'contact_info'
:
{
'company_name'
:
self
.
company_name
,
'company_contact_name'
:
self
.
company_contact_name
,
'company_contact_email'
:
self
.
company_contact_email
,
'recipient_name'
:
self
.
recipient_name
,
'recipient_email'
:
self
.
recipient_email
,
'address_line_1'
:
self
.
address_line_1
,
'address_line_2'
:
self
.
address_line_2
,
'address_line_3'
:
self
.
address_line_3
,
'city'
:
self
.
city
,
'state'
:
self
.
state
,
'zip'
:
self
.
zip
,
'country'
:
self
.
country
,
},
'items'
:
[
item
.
snapshot
()
for
item
in
InvoiceItem
.
objects
.
filter
(
invoice
=
self
)
.
select_subclasses
()
],
'transactions'
:
[
trans
.
snapshot
()
for
trans
in
InvoiceTransaction
.
objects
.
filter
(
invoice
=
self
)
],
}
def
__unicode__
(
self
):
label
=
(
unicode
(
self
.
internal_reference
)
...
...
@@ -935,6 +980,24 @@ class InvoiceTransaction(TimeStampedModel):
created_by
=
models
.
ForeignKey
(
User
)
last_modified_by
=
models
.
ForeignKey
(
User
,
related_name
=
'last_modified_by_user'
)
def
snapshot
(
self
):
"""Create a snapshot of the invoice transaction.
The returned dictionary is JSON-serializable.
Returns:
dict
"""
return
{
'amount'
:
unicode
(
self
.
amount
),
'currency'
:
self
.
currency
,
'comments'
:
self
.
comments
,
'status'
:
self
.
status
,
'created_by'
:
self
.
created_by
.
username
,
# pylint: disable=no-member
'last_modified_by'
:
self
.
last_modified_by
.
username
# pylint: disable=no-member
}
class
InvoiceItem
(
TimeStampedModel
):
"""
...
...
@@ -964,6 +1027,21 @@ class InvoiceItem(TimeStampedModel):
help_text
=
ugettext_lazy
(
"Lower-case ISO currency codes"
)
)
def
snapshot
(
self
):
"""Create a snapshot of the invoice item.
The returned dictionary is JSON-serializable.
Returns:
dict
"""
return
{
'qty'
:
self
.
qty
,
'unit_price'
:
unicode
(
self
.
unit_price
),
'currency'
:
self
.
currency
}
class
CourseRegistrationCodeInvoiceItem
(
InvoiceItem
):
"""
...
...
@@ -973,6 +1051,89 @@ class CourseRegistrationCodeInvoiceItem(InvoiceItem):
"""
course_id
=
CourseKeyField
(
max_length
=
128
,
db_index
=
True
)
def
snapshot
(
self
):
"""Create a snapshot of the invoice item.
This is the same as a snapshot for other invoice items,
with the addition of a `course_id` field.
Returns:
dict
"""
snapshot
=
super
(
CourseRegistrationCodeInvoiceItem
,
self
)
.
snapshot
()
snapshot
[
'course_id'
]
=
unicode
(
self
.
course_id
)
return
snapshot
class
InvoiceHistory
(
models
.
Model
):
"""History of changes to invoices.
This table stores snapshots of invoice state,
including the associated line items and transactions
(payments/refunds).
Entries in the table are created, but never deleted
or modified.
We use Django signals to save history entries on change
events. These signals are fired within a database
transaction, so the history record is created only
if the invoice change is successfully persisted.
"""
timestamp
=
models
.
DateTimeField
(
auto_now_add
=
True
,
db_index
=
True
)
invoice
=
models
.
ForeignKey
(
Invoice
)
# JSON-serialized representation of the current state
# of the invoice, including its line items and
# transactions (payments/refunds).
snapshot
=
models
.
TextField
(
blank
=
True
)
@classmethod
def
save_invoice_snapshot
(
cls
,
invoice
):
"""Save a snapshot of the invoice's current state.
Arguments:
invoice (Invoice): The invoice to save.
"""
cls
.
objects
.
create
(
invoice
=
invoice
,
snapshot
=
json
.
dumps
(
invoice
.
snapshot
())
)
@staticmethod
def
snapshot_receiver
(
sender
,
instance
,
**
kwargs
):
# pylint: disable=unused-argument
"""Signal receiver that saves a snapshot of an invoice.
Arguments:
sender: Not used, but required by Django signals.
instance (Invoice, InvoiceItem, or InvoiceTransaction)
"""
if
isinstance
(
instance
,
Invoice
):
InvoiceHistory
.
save_invoice_snapshot
(
instance
)
elif
hasattr
(
instance
,
'invoice'
):
InvoiceHistory
.
save_invoice_snapshot
(
instance
.
invoice
)
class
Meta
:
# pylint: disable=missing-docstring,old-style-class
get_latest_by
=
"timestamp"
# Hook up Django signals to record changes in the history table.
# We record any change to an invoice, invoice item, or transaction.
# We also record any deletion of a transaction, since users can delete
# transactions via Django admin.
# Note that we need to include *each* InvoiceItem subclass
# here, since Django signals do not fire automatically for subclasses
# of the "sender" class.
post_save
.
connect
(
InvoiceHistory
.
snapshot_receiver
,
sender
=
Invoice
)
post_save
.
connect
(
InvoiceHistory
.
snapshot_receiver
,
sender
=
InvoiceItem
)
post_save
.
connect
(
InvoiceHistory
.
snapshot_receiver
,
sender
=
CourseRegistrationCodeInvoiceItem
)
post_save
.
connect
(
InvoiceHistory
.
snapshot_receiver
,
sender
=
InvoiceTransaction
)
post_delete
.
connect
(
InvoiceHistory
.
snapshot_receiver
,
sender
=
InvoiceTransaction
)
class
CourseRegistrationCode
(
models
.
Model
):
"""
...
...
lms/djangoapps/shoppingcart/tests/test_models.py
View file @
847f56d1
...
...
@@ -4,6 +4,8 @@ Tests for the Shopping Cart Models
from
decimal
import
Decimal
import
datetime
import
sys
import
json
import
copy
import
smtplib
from
boto.exception
import
BotoServerError
# this is a super-class of SESError and catches connection errors
...
...
@@ -18,15 +20,14 @@ from django.db import DatabaseError
from
django.test
import
TestCase
from
django.test.utils
import
override_settings
from
django.contrib.auth.models
import
AnonymousUser
from
xmodule.modulestore.tests.django_utils
import
(
ModuleStoreTestCase
,
mixed_store_config
)
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
shoppingcart.models
import
(
Order
,
OrderItem
,
CertificateItem
,
InvalidCartItem
,
CourseRegistrationCode
,
PaidCourseRegistration
,
CourseRegCodeItem
,
Donation
,
OrderItemSubclassPK
Donation
,
OrderItemSubclassPK
,
Invoice
,
CourseRegistrationCodeInvoiceItem
,
InvoiceTransaction
,
InvoiceHistory
)
from
student.tests.factories
import
UserFactory
from
student.models
import
CourseEnrollment
...
...
@@ -875,3 +876,164 @@ class DonationTest(ModuleStoreTestCase):
# Verify that the donation is marked as purchased
donation
=
Donation
.
objects
.
get
(
pk
=
donation
.
id
)
self
.
assertEqual
(
donation
.
status
,
"purchased"
)
class
InvoiceHistoryTest
(
TestCase
):
"""Tests for the InvoiceHistory model. """
INVOICE_INFO
=
{
'is_valid'
:
True
,
'internal_reference'
:
'Test Internal Ref Num'
,
'customer_reference_number'
:
'Test Customer Ref Num'
,
}
CONTACT_INFO
=
{
'company_name'
:
'Test Company'
,
'company_contact_name'
:
'Test Company Contact Name'
,
'company_contact_email'
:
'test-contact@example.com'
,
'recipient_name'
:
'Test Recipient Name'
,
'recipient_email'
:
'test-recipient@example.com'
,
'address_line_1'
:
'Test Address 1'
,
'address_line_2'
:
'Test Address 2'
,
'address_line_3'
:
'Test Address 3'
,
'city'
:
'Test City'
,
'state'
:
'Test State'
,
'zip'
:
'12345'
,
'country'
:
'US'
,
}
def
setUp
(
self
):
invoice_data
=
copy
.
copy
(
self
.
INVOICE_INFO
)
invoice_data
.
update
(
self
.
CONTACT_INFO
)
self
.
invoice
=
Invoice
.
objects
.
create
(
total_amount
=
"123.45"
,
**
invoice_data
)
self
.
course_key
=
CourseLocator
(
'edX'
,
'DemoX'
,
'Demo_Course'
)
self
.
user
=
UserFactory
.
create
()
def
test_invoice_contact_info_history
(
self
):
self
.
_assert_history_invoice_info
(
is_valid
=
True
,
internal_ref
=
self
.
INVOICE_INFO
[
'internal_reference'
],
customer_ref
=
self
.
INVOICE_INFO
[
'customer_reference_number'
]
)
self
.
_assert_history_contact_info
(
**
self
.
CONTACT_INFO
)
self
.
_assert_history_items
([])
self
.
_assert_history_transactions
([])
def
test_invoice_history_items
(
self
):
# Create an invoice item
CourseRegistrationCodeInvoiceItem
.
objects
.
create
(
invoice
=
self
.
invoice
,
qty
=
1
,
unit_price
=
'123.45'
,
course_id
=
self
.
course_key
)
self
.
_assert_history_items
([{
'qty'
:
1
,
'unit_price'
:
'123.45'
,
'currency'
:
'usd'
,
'course_id'
:
unicode
(
self
.
course_key
)
}])
# Create a second invoice item
CourseRegistrationCodeInvoiceItem
.
objects
.
create
(
invoice
=
self
.
invoice
,
qty
=
2
,
unit_price
=
'456.78'
,
course_id
=
self
.
course_key
)
self
.
_assert_history_items
([
{
'qty'
:
1
,
'unit_price'
:
'123.45'
,
'currency'
:
'usd'
,
'course_id'
:
unicode
(
self
.
course_key
)
},
{
'qty'
:
2
,
'unit_price'
:
'456.78'
,
'currency'
:
'usd'
,
'course_id'
:
unicode
(
self
.
course_key
)
}
])
def
test_invoice_history_transactions
(
self
):
# Create an invoice transaction
first_transaction
=
InvoiceTransaction
.
objects
.
create
(
invoice
=
self
.
invoice
,
amount
=
'123.45'
,
currency
=
'usd'
,
comments
=
'test comments'
,
status
=
'completed'
,
created_by
=
self
.
user
,
last_modified_by
=
self
.
user
)
self
.
_assert_history_transactions
([{
'amount'
:
'123.45'
,
'currency'
:
'usd'
,
'comments'
:
'test comments'
,
'status'
:
'completed'
,
'created_by'
:
self
.
user
.
username
,
'last_modified_by'
:
self
.
user
.
username
,
}])
# Create a second invoice transaction
second_transaction
=
InvoiceTransaction
.
objects
.
create
(
invoice
=
self
.
invoice
,
amount
=
'456.78'
,
currency
=
'usd'
,
comments
=
'test more comments'
,
status
=
'started'
,
created_by
=
self
.
user
,
last_modified_by
=
self
.
user
)
self
.
_assert_history_transactions
([
{
'amount'
:
'123.45'
,
'currency'
:
'usd'
,
'comments'
:
'test comments'
,
'status'
:
'completed'
,
'created_by'
:
self
.
user
.
username
,
'last_modified_by'
:
self
.
user
.
username
,
},
{
'amount'
:
'456.78'
,
'currency'
:
'usd'
,
'comments'
:
'test more comments'
,
'status'
:
'started'
,
'created_by'
:
self
.
user
.
username
,
'last_modified_by'
:
self
.
user
.
username
,
}
])
# Delete the transactions
first_transaction
.
delete
()
second_transaction
.
delete
()
self
.
_assert_history_transactions
([])
def
_assert_history_invoice_info
(
self
,
is_valid
=
True
,
customer_ref
=
None
,
internal_ref
=
None
):
"""Check top-level invoice information in the latest history record. """
latest
=
self
.
_latest_history
()
self
.
assertEqual
(
latest
[
'is_valid'
],
is_valid
)
self
.
assertEqual
(
latest
[
'customer_reference'
],
customer_ref
)
self
.
assertEqual
(
latest
[
'internal_reference'
],
internal_ref
)
def
_assert_history_contact_info
(
self
,
**
kwargs
):
"""Check contact info in the latest history record. """
contact_info
=
self
.
_latest_history
()[
'contact_info'
]
for
key
,
value
in
kwargs
.
iteritems
():
self
.
assertEqual
(
contact_info
[
key
],
value
)
def
_assert_history_items
(
self
,
expected_items
):
"""Check line item info in the latest history record. """
items
=
self
.
_latest_history
()[
'items'
]
self
.
assertItemsEqual
(
items
,
expected_items
)
def
_assert_history_transactions
(
self
,
expected_transactions
):
"""Check transactions (payments/refunds) in the latest history record. """
transactions
=
self
.
_latest_history
()[
'transactions'
]
self
.
assertItemsEqual
(
transactions
,
expected_transactions
)
def
_latest_history
(
self
):
"""Retrieve the snapshot from the latest history record. """
latest
=
InvoiceHistory
.
objects
.
latest
()
return
json
.
loads
(
latest
.
snapshot
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment