ECOM-890: Update invoice data model.
ECOM-891: Allow tracking of invoice transactions. Authors: Awais Qureshi and Aamir Khan
This commit is contained in:
@@ -299,8 +299,14 @@ class DashboardTest(ModuleStoreTestCase):
|
||||
recipient_name='Testw_1', recipient_email='test2@test.com', internal_reference="A",
|
||||
course_id=self.course.id, is_valid=False
|
||||
)
|
||||
invoice_item = shoppingcart.models.CourseRegistrationCodeInvoiceItem.objects.create(
|
||||
invoice=sale_invoice_1,
|
||||
qty=1,
|
||||
unit_price=1234.32,
|
||||
course_id=self.course.id
|
||||
)
|
||||
course_reg_code = shoppingcart.models.CourseRegistrationCode(
|
||||
code="abcde", course_id=self.course.id, created_by=self.user, invoice=sale_invoice_1, mode_slug='honor'
|
||||
code="abcde", course_id=self.course.id, created_by=self.user, invoice=sale_invoice_1, invoice_item=invoice_item, mode_slug='honor'
|
||||
)
|
||||
course_reg_code.save()
|
||||
|
||||
|
||||
@@ -465,8 +465,8 @@ def is_course_blocked(request, redeemed_registration_codes, course_key):
|
||||
# registration codes may be generated via Bulk Purchase Scenario
|
||||
# we have to check only for the invoice generated registration codes
|
||||
# that their invoice is valid or not
|
||||
if redeemed_registration.invoice:
|
||||
if not getattr(redeemed_registration.invoice, 'is_valid'):
|
||||
if redeemed_registration.invoice_item:
|
||||
if not getattr(redeemed_registration.invoice_item.invoice, 'is_valid'):
|
||||
blocked = True
|
||||
# disabling email notifications for unpaid registration courses
|
||||
Optout.objects.get_or_create(user=request.user, course_id=course_key)
|
||||
|
||||
@@ -39,7 +39,7 @@ from django_comment_common.utils import seed_permissions_roles
|
||||
from microsite_configuration import microsite
|
||||
from shoppingcart.models import (
|
||||
RegistrationCodeRedemption, Order, CouponRedemption,
|
||||
PaidCourseRegistration, Coupon, Invoice, CourseRegistrationCode
|
||||
PaidCourseRegistration, Coupon, Invoice, CourseRegistrationCode, CourseRegistrationCodeInvoiceItem
|
||||
)
|
||||
from shoppingcart.pdf import PDFInvoice
|
||||
from student.models import (
|
||||
@@ -1713,6 +1713,12 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
recipient_name='Testw', recipient_email='test1@test.com', customer_reference_number='2Fwe23S',
|
||||
internal_reference="A", course_id=self.course.id, is_valid=True
|
||||
)
|
||||
self.invoice_item = CourseRegistrationCodeInvoiceItem.objects.create(
|
||||
invoice=self.sale_invoice_1,
|
||||
qty=1,
|
||||
unit_price=1234.32,
|
||||
course_id=self.course.id
|
||||
)
|
||||
|
||||
self.students = [UserFactory() for _ in xrange(6)]
|
||||
for student in self.students:
|
||||
@@ -1724,8 +1730,12 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
"""
|
||||
for i in range(2):
|
||||
course_registration_code = CourseRegistrationCode(
|
||||
code='sale_invoice{}'.format(i), course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor, invoice=self.sale_invoice_1, mode_slug='honor'
|
||||
code='sale_invoice{}'.format(i),
|
||||
course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice_1,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
)
|
||||
course_registration_code.save()
|
||||
|
||||
@@ -1745,7 +1755,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
data['event_type'] = "re_validate"
|
||||
self.assert_request_status_code(200, url, method="POST", data=data)
|
||||
|
||||
# Now re_validate the same actove invoice number and expect an Bad request
|
||||
# Now re_validate the same active invoice number and expect an Bad request
|
||||
response = self.assert_request_status_code(400, url, method="POST", data=data)
|
||||
self.assertIn("This invoice is already active.", response.content)
|
||||
|
||||
@@ -1844,12 +1854,19 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
"""
|
||||
for i in range(2):
|
||||
course_registration_code = CourseRegistrationCode(
|
||||
code='sale_invoice{}'.format(i), course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor, invoice=self.sale_invoice_1, mode_slug='honor'
|
||||
code='sale_invoice{}'.format(i),
|
||||
course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice_1,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
)
|
||||
course_registration_code.save()
|
||||
|
||||
url = reverse('get_sale_records', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
url = reverse(
|
||||
'get_sale_records',
|
||||
kwargs={'course_id': self.course.id.to_deprecated_string()}
|
||||
)
|
||||
response = self.client.get(url + '/csv', {})
|
||||
self.assertEqual(response['Content-Type'], 'text/csv')
|
||||
|
||||
@@ -1859,8 +1876,12 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
"""
|
||||
for i in range(5):
|
||||
course_registration_code = CourseRegistrationCode(
|
||||
code='sale_invoice{}'.format(i), course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor, invoice=self.sale_invoice_1, mode_slug='honor'
|
||||
code='sale_invoice{}'.format(i),
|
||||
course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice_1,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
)
|
||||
course_registration_code.save()
|
||||
|
||||
@@ -1870,7 +1891,13 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
self.assertIn('sale', res_json)
|
||||
|
||||
for res in res_json['sale']:
|
||||
self.validate_sale_records_response(res, course_registration_code, self.sale_invoice_1, 0)
|
||||
self.validate_sale_records_response(
|
||||
res,
|
||||
course_registration_code,
|
||||
self.sale_invoice_1,
|
||||
0,
|
||||
invoice_item=self.invoice_item
|
||||
)
|
||||
|
||||
def test_get_sale_records_features_with_multiple_invoices(self):
|
||||
"""
|
||||
@@ -1878,8 +1905,12 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
"""
|
||||
for i in range(5):
|
||||
course_registration_code = CourseRegistrationCode(
|
||||
code='qwerty{}'.format(i), course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor, invoice=self.sale_invoice_1, mode_slug='honor'
|
||||
code='qwerty{}'.format(i),
|
||||
course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor,
|
||||
invoice=self.sale_invoice_1,
|
||||
invoice_item=self.invoice_item,
|
||||
mode_slug='honor'
|
||||
)
|
||||
course_registration_code.save()
|
||||
|
||||
@@ -1890,10 +1921,17 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
internal_reference="B", course_id=self.course.id
|
||||
)
|
||||
|
||||
invoice_item_2 = CourseRegistrationCodeInvoiceItem.objects.create(
|
||||
invoice=sale_invoice_2,
|
||||
qty=1,
|
||||
unit_price=1234.32,
|
||||
course_id=self.course.id
|
||||
)
|
||||
|
||||
for i in range(5):
|
||||
course_registration_code = CourseRegistrationCode(
|
||||
code='xyzmn{}'.format(i), course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor, invoice=sale_invoice_2, mode_slug='honor'
|
||||
created_by=self.instructor, invoice=sale_invoice_2, invoice_item=invoice_item_2, mode_slug='honor'
|
||||
)
|
||||
course_registration_code.save()
|
||||
|
||||
@@ -1902,10 +1940,22 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
res_json = json.loads(response.content)
|
||||
self.assertIn('sale', res_json)
|
||||
|
||||
self.validate_sale_records_response(res_json['sale'][0], course_registration_code, self.sale_invoice_1, 0)
|
||||
self.validate_sale_records_response(res_json['sale'][1], course_registration_code, sale_invoice_2, 0)
|
||||
self.validate_sale_records_response(
|
||||
res_json['sale'][0],
|
||||
course_registration_code,
|
||||
self.sale_invoice_1,
|
||||
0,
|
||||
invoice_item=self.invoice_item
|
||||
)
|
||||
self.validate_sale_records_response(
|
||||
res_json['sale'][1],
|
||||
course_registration_code,
|
||||
sale_invoice_2,
|
||||
0,
|
||||
invoice_item=invoice_item_2
|
||||
)
|
||||
|
||||
def validate_sale_records_response(self, res, course_registration_code, invoice, used_codes):
|
||||
def validate_sale_records_response(self, res, course_registration_code, invoice, used_codes, invoice_item):
|
||||
"""
|
||||
validate sale records attribute values with the response object
|
||||
"""
|
||||
@@ -1919,7 +1969,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
|
||||
self.assertEqual(res['customer_reference_number'], invoice.customer_reference_number)
|
||||
self.assertEqual(res['invoice_number'], invoice.id)
|
||||
self.assertEqual(res['created_by'], course_registration_code.created_by.username)
|
||||
self.assertEqual(res['course_id'], invoice.course_id.to_deprecated_string())
|
||||
self.assertEqual(res['course_id'], invoice_item.course_id.to_deprecated_string())
|
||||
self.assertEqual(res['total_used_codes'], used_codes)
|
||||
self.assertEqual(res['total_codes'], 5)
|
||||
|
||||
@@ -3011,7 +3061,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 12, 'company_name': 'Test Group', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street',
|
||||
'address_line_2': '', 'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3043,7 +3093,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 5, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 121.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 121.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': 'True'
|
||||
@@ -3065,7 +3115,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 5, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 121.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 121.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': 'True'
|
||||
@@ -3107,7 +3157,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 15, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3129,7 +3179,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 15, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3165,7 +3215,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
coupon.save()
|
||||
data = {
|
||||
'total_registration_codes': 3, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3189,7 +3239,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 2, 'company_name': 'Test Group', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3225,7 +3275,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 9, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'sale_price': 122.45, 'company_contact_email': 'Test@company.com', 'recipient_name': 'Test123',
|
||||
'unit_price': 122.45, 'company_contact_email': 'Test@company.com', 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3276,7 +3326,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 9, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3315,7 +3365,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 9, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3342,7 +3392,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
)
|
||||
data = {
|
||||
'total_registration_codes': 9, 'company_name': 'Group Alpha', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
@@ -3361,7 +3411,7 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 5.5, 'company_name': 'Group Invoice', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': True
|
||||
@@ -3379,6 +3429,26 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
|
||||
body = response.content.replace('\r', '')
|
||||
self.assertTrue(body.startswith(EXPECTED_CSV_HEADER))
|
||||
|
||||
def test_with_invalid_unit_price(self):
|
||||
"""
|
||||
Test to generate a response of all the course registration codes
|
||||
"""
|
||||
generate_code_url = reverse(
|
||||
'generate_registration_codes', kwargs={'course_id': self.course.id.to_deprecated_string()}
|
||||
)
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 10, 'company_name': 'Group Invoice', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 'invalid', 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': True
|
||||
}
|
||||
|
||||
response = self.client.post(generate_code_url, data, **{'HTTP_HOST': 'localhost'})
|
||||
self.assertEqual(response.status_code, 400, response.content)
|
||||
self.assertIn('Could not parse amount as', response.content)
|
||||
|
||||
def test_get_historical_coupon_codes(self):
|
||||
"""
|
||||
Test to download a response of all the active coupon codes
|
||||
|
||||
@@ -27,6 +27,7 @@ import string # pylint: disable=deprecated-module
|
||||
import random
|
||||
import unicodecsv
|
||||
import urllib
|
||||
import decimal
|
||||
from student import auth
|
||||
from student.roles import CourseSalesAdminRole
|
||||
from util.file import store_uploaded_file, course_and_time_based_filename_generator, FileValidationException, UniversalNewlineIterator
|
||||
@@ -49,7 +50,14 @@ from django_comment_common.models import (
|
||||
)
|
||||
from edxmako.shortcuts import render_to_response, render_to_string
|
||||
from courseware.models import StudentModule
|
||||
from shoppingcart.models import Coupon, CourseRegistrationCode, RegistrationCodeRedemption, Invoice, CourseMode
|
||||
from shoppingcart.models import (
|
||||
Coupon,
|
||||
CourseRegistrationCode,
|
||||
RegistrationCodeRedemption,
|
||||
Invoice,
|
||||
CourseMode,
|
||||
CourseRegistrationCodeInvoiceItem,
|
||||
)
|
||||
from student.models import CourseEnrollment, unique_id_for_user, anonymous_id_for_user
|
||||
import instructor_task.api
|
||||
from instructor_task.api_helper import AlreadyRunningError
|
||||
@@ -885,9 +893,13 @@ def sale_validation(request, course_id):
|
||||
|
||||
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
try:
|
||||
obj_invoice = Invoice.objects.select_related('is_valid').get(id=invoice_number, course_id=course_id)
|
||||
except Invoice.DoesNotExist:
|
||||
return HttpResponseNotFound(_("Invoice number '{0}' does not exist.").format(invoice_number))
|
||||
obj_invoice = CourseRegistrationCodeInvoiceItem.objects.select_related('invoice').get(
|
||||
invoice_id=invoice_number,
|
||||
course_id=course_id
|
||||
)
|
||||
obj_invoice = obj_invoice.invoice
|
||||
except CourseRegistrationCodeInvoiceItem.DoesNotExist: # Check for old type invoices
|
||||
return HttpResponseNotFound(_("Invoice number '{0}' does not exist.".format(invoice_number)))
|
||||
|
||||
if event_type == "invalidate":
|
||||
return invalidate_invoice(obj_invoice)
|
||||
@@ -1053,7 +1065,7 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument
|
||||
return instructor_analytics.csvs.create_csv_response('Coupons.csv', header, data_rows)
|
||||
|
||||
|
||||
def save_registration_code(user, course_id, mode_slug, invoice=None, order=None):
|
||||
def save_registration_code(user, course_id, mode_slug, invoice=None, order=None, invoice_item=None):
|
||||
"""
|
||||
recursive function that generate a new code every time and saves in the Course Registration Table
|
||||
if validation check passes
|
||||
@@ -1064,6 +1076,7 @@ def save_registration_code(user, course_id, mode_slug, invoice=None, order=None)
|
||||
mode_slug (str): The Course Mode Slug associated with any enrollment made by these codes.
|
||||
invoice (Invoice): (Optional) The associated invoice for this code.
|
||||
order (Order): (Optional) The associated order for this code.
|
||||
invoice_item (CourseRegistrationCodeInvoiceItem) : (Optional) The associated CourseRegistrationCodeInvoiceItem
|
||||
|
||||
Returns:
|
||||
The newly created CourseRegistrationCode.
|
||||
@@ -1074,7 +1087,9 @@ def save_registration_code(user, course_id, mode_slug, invoice=None, order=None)
|
||||
# check if the generated code is in the Coupon Table
|
||||
matching_coupons = Coupon.objects.filter(code=code, is_active=True)
|
||||
if matching_coupons:
|
||||
return save_registration_code(user, course_id, invoice, order)
|
||||
return save_registration_code(
|
||||
user, course_id, mode_slug, invoice=invoice, order=order, invoice_item=invoice_item
|
||||
)
|
||||
|
||||
course_registration = CourseRegistrationCode(
|
||||
code=code,
|
||||
@@ -1082,13 +1097,16 @@ def save_registration_code(user, course_id, mode_slug, invoice=None, order=None)
|
||||
created_by=user,
|
||||
invoice=invoice,
|
||||
order=order,
|
||||
mode_slug=mode_slug
|
||||
mode_slug=mode_slug,
|
||||
invoice_item=invoice_item
|
||||
)
|
||||
try:
|
||||
course_registration.save()
|
||||
return course_registration
|
||||
except IntegrityError:
|
||||
return save_registration_code(user, course_id, invoice, order)
|
||||
return save_registration_code(
|
||||
user, course_id, mode_slug, invoice=invoice, order=order, invoice_item=invoice_item
|
||||
)
|
||||
|
||||
|
||||
def registration_codes_csv(file_name, codes_list, csv_type=None):
|
||||
@@ -1130,11 +1148,13 @@ def get_registration_codes(request, course_id): # pylint: disable=unused-argume
|
||||
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
|
||||
#filter all the course registration codes
|
||||
registration_codes = CourseRegistrationCode.objects.filter(course_id=course_id).order_by('invoice__company_name')
|
||||
registration_codes = CourseRegistrationCode.objects.filter(
|
||||
course_id=course_id
|
||||
).order_by('invoice_item__invoice__company_name')
|
||||
|
||||
company_name = request.POST['download_company_name']
|
||||
if company_name:
|
||||
registration_codes = registration_codes.filter(invoice__company_name=company_name)
|
||||
registration_codes = registration_codes.filter(invoice_item__invoice__company_name=company_name)
|
||||
|
||||
csv_type = 'download'
|
||||
return registration_codes_csv("Registration_Codes.csv", registration_codes, csv_type)
|
||||
@@ -1160,7 +1180,21 @@ def generate_registration_codes(request, course_id):
|
||||
company_name = request.POST['company_name']
|
||||
company_contact_name = request.POST['company_contact_name']
|
||||
company_contact_email = request.POST['company_contact_email']
|
||||
sale_price = request.POST['sale_price']
|
||||
unit_price = request.POST['unit_price']
|
||||
|
||||
try:
|
||||
unit_price = (
|
||||
decimal.Decimal(unit_price)
|
||||
).quantize(
|
||||
decimal.Decimal('.01'),
|
||||
rounding=decimal.ROUND_DOWN
|
||||
)
|
||||
except decimal.InvalidOperation:
|
||||
return HttpResponse(
|
||||
status=400,
|
||||
content=_(u"Could not parse amount as a decimal")
|
||||
)
|
||||
|
||||
recipient_name = request.POST['recipient_name']
|
||||
recipient_email = request.POST['recipient_email']
|
||||
address_line_1 = request.POST['address_line_1']
|
||||
@@ -1177,6 +1211,7 @@ def generate_registration_codes(request, course_id):
|
||||
recipient_list.append(request.user.email)
|
||||
invoice_copy = True
|
||||
|
||||
sale_price = unit_price * course_code_number
|
||||
UserPreference.set_preference(request.user, INVOICE_KEY, invoice_copy)
|
||||
sale_invoice = Invoice.objects.create(
|
||||
total_amount=sale_price,
|
||||
@@ -1197,6 +1232,13 @@ def generate_registration_codes(request, course_id):
|
||||
customer_reference_number=customer_reference_number
|
||||
)
|
||||
|
||||
invoice_item = CourseRegistrationCodeInvoiceItem.objects.create(
|
||||
invoice=sale_invoice,
|
||||
qty=course_code_number,
|
||||
unit_price=unit_price,
|
||||
course_id=course_id
|
||||
)
|
||||
|
||||
course = get_course_by_id(course_id, depth=0)
|
||||
paid_modes = CourseMode.paid_modes_for_course(course_id)
|
||||
|
||||
@@ -1217,7 +1259,7 @@ def generate_registration_codes(request, course_id):
|
||||
registration_codes = []
|
||||
for __ in range(course_code_number): # pylint: disable=redefined-outer-name
|
||||
generated_registration_code = save_registration_code(
|
||||
request.user, course_id, course_mode.slug, sale_invoice, order=None
|
||||
request.user, course_id, course_mode.slug, invoice=sale_invoice, order=None, invoice_item=invoice_item
|
||||
)
|
||||
registration_codes.append(generated_registration_code)
|
||||
|
||||
@@ -1309,13 +1351,17 @@ def active_registration_codes(request, course_id): # pylint: disable=unused-arg
|
||||
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
|
||||
# find all the registration codes in this course
|
||||
registration_codes_list = CourseRegistrationCode.objects.filter(course_id=course_id).order_by('invoice__company_name')
|
||||
registration_codes_list = CourseRegistrationCode.objects.filter(
|
||||
course_id=course_id
|
||||
).order_by('invoice_item__invoice__company_name')
|
||||
|
||||
company_name = request.POST['active_company_name']
|
||||
if company_name:
|
||||
registration_codes_list = registration_codes_list.filter(invoice__company_name=company_name)
|
||||
registration_codes_list = registration_codes_list.filter(invoice_item__invoice__company_name=company_name)
|
||||
# find the redeemed registration codes if any exist in the db
|
||||
code_redemption_set = RegistrationCodeRedemption.objects.select_related('registration_code').filter(registration_code__course_id=course_id)
|
||||
code_redemption_set = RegistrationCodeRedemption.objects.select_related(
|
||||
'registration_code', 'registration_code__invoice_item__invoice'
|
||||
).filter(registration_code__course_id=course_id)
|
||||
if code_redemption_set.exists():
|
||||
redeemed_registration_codes = [code.registration_code.code for code in code_redemption_set]
|
||||
# exclude the redeemed registration codes from the registration codes list and you will get
|
||||
@@ -1346,11 +1392,11 @@ def spent_registration_codes(request, course_id): # pylint: disable=unused-argu
|
||||
# you will get a list of all the spent(Redeemed) Registration Codes
|
||||
spent_codes_list = CourseRegistrationCode.objects.filter(
|
||||
course_id=course_id, code__in=redeemed_registration_codes
|
||||
).order_by('invoice__company_name')
|
||||
).order_by('invoice_item__invoice__company_name').select_related('invoice_item__invoice')
|
||||
|
||||
company_name = request.POST['spent_company_name']
|
||||
if company_name:
|
||||
spent_codes_list = spent_codes_list.filter(invoice__company_name=company_name) # pylint: disable=maybe-no-member
|
||||
spent_codes_list = spent_codes_list.filter(invoice_item__invoice__company_name=company_name) # pylint: disable=maybe-no-member
|
||||
|
||||
csv_type = 'spent'
|
||||
return registration_codes_csv("Spent_Registration_Codes.csv", spent_codes_list, csv_type)
|
||||
|
||||
@@ -6,7 +6,7 @@ Serve miscellaneous course and student data
|
||||
import json
|
||||
from shoppingcart.models import (
|
||||
PaidCourseRegistration, CouponRedemption, Invoice, CourseRegCodeItem,
|
||||
OrderTypes, RegistrationCodeRedemption, CourseRegistrationCode
|
||||
OrderTypes, RegistrationCodeRedemption, CourseRegistrationCode, CourseRegistrationCodeInvoiceItem
|
||||
)
|
||||
from django.db.models import Q
|
||||
from django.conf import settings
|
||||
@@ -110,22 +110,25 @@ def sale_record_features(course_id, features):
|
||||
{'company_name': 'group_C', 'total_codes': '3', total_amount:'total_amount3 in decimal'.}
|
||||
]
|
||||
"""
|
||||
sales = Invoice.objects.filter(course_id=course_id)
|
||||
sales = CourseRegistrationCodeInvoiceItem.objects.select_related('invoice').filter(course_id=course_id)
|
||||
|
||||
def sale_records_info(sale, features):
|
||||
""" convert sales records to dictionary """
|
||||
"""
|
||||
Convert sales records to dictionary
|
||||
|
||||
"""
|
||||
invoice = sale.invoice
|
||||
sale_features = [x for x in SALE_FEATURES if x in features]
|
||||
course_reg_features = [x for x in COURSE_REGISTRATION_FEATURES if x in features]
|
||||
|
||||
# Extracting sale information
|
||||
sale_dict = dict((feature, getattr(sale, feature))
|
||||
sale_dict = dict((feature, getattr(invoice, feature))
|
||||
for feature in sale_features)
|
||||
|
||||
total_used_codes = RegistrationCodeRedemption.objects.filter(
|
||||
registration_code__in=sale.courseregistrationcode_set.all()
|
||||
).count()
|
||||
sale_dict.update({"invoice_number": getattr(sale, 'id')})
|
||||
sale_dict.update({"invoice_number": getattr(invoice, 'id')})
|
||||
sale_dict.update({"total_codes": sale.courseregistrationcode_set.all().count()})
|
||||
sale_dict.update({'total_used_codes': total_used_codes})
|
||||
|
||||
@@ -261,11 +264,11 @@ def course_registration_features(features, registration_codes, csv_type):
|
||||
|
||||
course_registration_dict = dict((feature, getattr(registration_code, feature)) for feature in registration_features)
|
||||
course_registration_dict['company_name'] = None
|
||||
if registration_code.invoice:
|
||||
course_registration_dict['company_name'] = getattr(registration_code.invoice, 'company_name')
|
||||
if registration_code.invoice_item:
|
||||
course_registration_dict['company_name'] = getattr(registration_code.invoice_item.invoice, 'company_name')
|
||||
course_registration_dict['redeemed_by'] = None
|
||||
if registration_code.invoice:
|
||||
sale_invoice = Invoice.objects.get(id=registration_code.invoice_id)
|
||||
if registration_code.invoice_item:
|
||||
sale_invoice = registration_code.invoice_item.invoice
|
||||
course_registration_dict['invoice_id'] = sale_invoice.id
|
||||
course_registration_dict['purchaser'] = sale_invoice.recipient_name
|
||||
course_registration_dict['customer_reference_number'] = sale_invoice.customer_reference_number
|
||||
|
||||
@@ -11,7 +11,7 @@ from student.tests.factories import UserFactory, CourseModeFactory
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from shoppingcart.models import (
|
||||
CourseRegistrationCode, RegistrationCodeRedemption, Order,
|
||||
Invoice, Coupon, CourseRegCodeItem, CouponRedemption
|
||||
Invoice, Coupon, CourseRegCodeItem, CouponRedemption, CourseRegistrationCodeInvoiceItem
|
||||
)
|
||||
from course_modes.models import CourseMode
|
||||
from instructor_analytics.basic import (
|
||||
@@ -147,10 +147,16 @@ class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase):
|
||||
company_contact_email='test@company.com', recipient_name='Testw_1', recipient_email='test2@test.com',
|
||||
customer_reference_number='2Fwe23S', internal_reference="ABC", course_id=self.course.id
|
||||
)
|
||||
invoice_item = CourseRegistrationCodeInvoiceItem.objects.create(
|
||||
invoice=sale_invoice,
|
||||
qty=1,
|
||||
unit_price=1234.32,
|
||||
course_id=self.course.id
|
||||
)
|
||||
for i in range(5):
|
||||
course_code = CourseRegistrationCode(
|
||||
code="test_code{}".format(i), course_id=self.course.id.to_deprecated_string(),
|
||||
created_by=self.instructor, invoice=sale_invoice, mode_slug='honor'
|
||||
created_by=self.instructor, invoice=sale_invoice, invoice_item=invoice_item, mode_slug='honor'
|
||||
)
|
||||
course_code.save()
|
||||
|
||||
@@ -272,7 +278,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
|
||||
kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 12, 'company_name': 'Test Group', 'sale_price': 122.45,
|
||||
'total_registration_codes': 12, 'company_name': 'Test Group', 'unit_price': 122.45,
|
||||
'company_contact_name': 'TestName', 'company_contact_email': 'test@company.com', 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street', 'address_line_2': '',
|
||||
'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
@@ -306,11 +312,17 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
|
||||
)
|
||||
self.assertIn(
|
||||
course_registration['company_name'],
|
||||
[getattr(registration_code.invoice, 'company_name') for registration_code in registration_codes]
|
||||
[
|
||||
getattr(registration_code.invoice_item.invoice, 'company_name')
|
||||
for registration_code in registration_codes
|
||||
]
|
||||
)
|
||||
self.assertIn(
|
||||
course_registration['invoice_id'],
|
||||
[registration_code.invoice_id for registration_code in registration_codes]
|
||||
[
|
||||
registration_code.invoice_item.invoice_id
|
||||
for registration_code in registration_codes
|
||||
]
|
||||
)
|
||||
|
||||
def test_coupon_codes_features(self):
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"""
|
||||
Allows django admin site to add PaidCourseRegistrationAnnotations
|
||||
"""
|
||||
"""Django admin interface for the shopping cart models. """
|
||||
from ratelimitbackend import admin
|
||||
from shoppingcart.models import (
|
||||
PaidCourseRegistrationAnnotation, Coupon, DonationConfiguration
|
||||
PaidCourseRegistrationAnnotation,
|
||||
Coupon,
|
||||
DonationConfiguration,
|
||||
Invoice,
|
||||
CourseRegistrationCodeInvoiceItem,
|
||||
InvoiceTransaction
|
||||
)
|
||||
|
||||
|
||||
@@ -49,6 +52,128 @@ class SoftDeleteCouponAdmin(admin.ModelAdmin):
|
||||
|
||||
really_delete_selected.short_description = "Delete s selected entries"
|
||||
|
||||
|
||||
class CourseRegistrationCodeInvoiceItemInline(admin.StackedInline):
|
||||
"""Admin for course registration code invoice items.
|
||||
|
||||
Displayed inline within the invoice admin UI.
|
||||
"""
|
||||
model = CourseRegistrationCodeInvoiceItem
|
||||
extra = 0
|
||||
can_delete = False
|
||||
readonly_fields = (
|
||||
'qty',
|
||||
'unit_price',
|
||||
'currency',
|
||||
'course_id',
|
||||
)
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
|
||||
class InvoiceTransactionInline(admin.StackedInline):
|
||||
"""Admin for invoice transactions.
|
||||
|
||||
Displayed inline within the invoice admin UI.
|
||||
"""
|
||||
model = InvoiceTransaction
|
||||
extra = 0
|
||||
readonly_fields = (
|
||||
'created',
|
||||
'modified',
|
||||
'created_by',
|
||||
'last_modified_by'
|
||||
)
|
||||
|
||||
|
||||
class InvoiceAdmin(admin.ModelAdmin):
|
||||
"""Admin for invoices.
|
||||
|
||||
This is intended for the internal finance team
|
||||
to be able to view and update invoice information,
|
||||
including payments and refunds.
|
||||
|
||||
"""
|
||||
date_hierarchy = 'created'
|
||||
can_delete = False
|
||||
readonly_fields = ('created', 'modified')
|
||||
search_fields = (
|
||||
'internal_reference',
|
||||
'customer_reference_number',
|
||||
'company_name',
|
||||
)
|
||||
fieldsets = (
|
||||
(
|
||||
None, {
|
||||
'fields': (
|
||||
'internal_reference',
|
||||
'customer_reference_number',
|
||||
'created',
|
||||
'modified',
|
||||
)
|
||||
}
|
||||
),
|
||||
(
|
||||
'Billing Information', {
|
||||
'fields': (
|
||||
'company_name',
|
||||
'company_contact_name',
|
||||
'company_contact_email',
|
||||
'recipient_name',
|
||||
'recipient_email',
|
||||
'address_line_1',
|
||||
'address_line_2',
|
||||
'address_line_3',
|
||||
'city',
|
||||
'state',
|
||||
'zip',
|
||||
'country'
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
readonly_fields = (
|
||||
'internal_reference',
|
||||
'customer_reference_number',
|
||||
'created',
|
||||
'modified',
|
||||
'company_name',
|
||||
'company_contact_name',
|
||||
'company_contact_email',
|
||||
'recipient_name',
|
||||
'recipient_email',
|
||||
'address_line_1',
|
||||
'address_line_2',
|
||||
'address_line_3',
|
||||
'city',
|
||||
'state',
|
||||
'zip',
|
||||
'country'
|
||||
)
|
||||
inlines = [
|
||||
CourseRegistrationCodeInvoiceItemInline,
|
||||
InvoiceTransactionInline
|
||||
]
|
||||
|
||||
def save_formset(self, request, form, formset, change):
|
||||
"""Save the user who created and modified invoice transactions. """
|
||||
instances = formset.save(commit=False)
|
||||
for instance in instances:
|
||||
if isinstance(instance, InvoiceTransaction):
|
||||
if not hasattr(instance, 'created_by'):
|
||||
instance.created_by = request.user
|
||||
instance.last_modified_by = request.user
|
||||
instance.save()
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
admin.site.register(PaidCourseRegistrationAnnotation)
|
||||
admin.site.register(Coupon, SoftDeleteCouponAdmin)
|
||||
admin.site.register(DonationConfiguration)
|
||||
admin.site.register(Invoice, InvoiceAdmin)
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
# -*- 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 'InvoiceTransaction'
|
||||
db.create_table('shoppingcart_invoicetransaction', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)),
|
||||
('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)),
|
||||
('invoice', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shoppingcart.Invoice'])),
|
||||
('amount', self.gf('django.db.models.fields.DecimalField')(default=0.0, max_digits=30, decimal_places=2)),
|
||||
('currency', self.gf('django.db.models.fields.CharField')(default='usd', max_length=8)),
|
||||
('comments', self.gf('django.db.models.fields.TextField')(null=True)),
|
||||
('status', self.gf('django.db.models.fields.CharField')(default='started', max_length=32)),
|
||||
('created_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
|
||||
('last_modified_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='last_modified_by_user', to=orm['auth.User'])),
|
||||
))
|
||||
db.send_create_signal('shoppingcart', ['InvoiceTransaction'])
|
||||
|
||||
# Adding model 'InvoiceItem'
|
||||
db.create_table('shoppingcart_invoiceitem', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)),
|
||||
('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)),
|
||||
('invoice', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shoppingcart.Invoice'])),
|
||||
('qty', self.gf('django.db.models.fields.IntegerField')(default=1)),
|
||||
('unit_price', self.gf('django.db.models.fields.DecimalField')(default=0.0, max_digits=30, decimal_places=2)),
|
||||
('currency', self.gf('django.db.models.fields.CharField')(default='usd', max_length=8)),
|
||||
))
|
||||
db.send_create_signal('shoppingcart', ['InvoiceItem'])
|
||||
|
||||
# Adding model 'CourseRegistrationCodeInvoiceItem'
|
||||
db.create_table('shoppingcart_courseregistrationcodeinvoiceitem', (
|
||||
('invoiceitem_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['shoppingcart.InvoiceItem'], unique=True, primary_key=True)),
|
||||
('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=128, db_index=True)),
|
||||
))
|
||||
db.send_create_signal('shoppingcart', ['CourseRegistrationCodeInvoiceItem'])
|
||||
|
||||
# Adding field 'CourseRegistrationCode.invoice_item'
|
||||
db.add_column('shoppingcart_courseregistrationcode', 'invoice_item',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shoppingcart.CourseRegistrationCodeInvoiceItem'], null=True),
|
||||
keep_default=True)
|
||||
|
||||
# Adding field 'Invoice.created'
|
||||
db.add_column('shoppingcart_invoice', 'created',
|
||||
self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now),
|
||||
keep_default=True)
|
||||
|
||||
# Adding field 'Invoice.modified'
|
||||
db.add_column('shoppingcart_invoice', 'modified',
|
||||
self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now),
|
||||
keep_default=True)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'InvoiceTransaction'
|
||||
db.delete_table('shoppingcart_invoicetransaction')
|
||||
|
||||
# Deleting model 'InvoiceItem'
|
||||
db.delete_table('shoppingcart_invoiceitem')
|
||||
|
||||
# Deleting model 'CourseRegistrationCodeInvoiceItem'
|
||||
db.delete_table('shoppingcart_courseregistrationcodeinvoiceitem')
|
||||
|
||||
# Deleting field 'CourseRegistrationCode.invoice_item'
|
||||
db.delete_column('shoppingcart_courseregistrationcode', 'invoice_item_id')
|
||||
|
||||
# Deleting field 'Invoice.created'
|
||||
db.delete_column('shoppingcart_invoice', 'created')
|
||||
|
||||
# Deleting field 'Invoice.modified'
|
||||
db.delete_column('shoppingcart_invoice', 'modified')
|
||||
|
||||
|
||||
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, 1, 27, 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, 1, 27, 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.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'}),
|
||||
'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, 1, 27, 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']
|
||||
270
lms/djangoapps/shoppingcart/migrations/0026_migrate_invoices.py
Normal file
270
lms/djangoapps/shoppingcart/migrations/0026_migrate_invoices.py
Normal file
@@ -0,0 +1,270 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Select all the invoices and number of registration codes(as qty) associated with every invoice
|
||||
invoices = orm.Invoice.objects.extra(
|
||||
select={
|
||||
'qty': 'SELECT COUNT(*) FROM shoppingcart_courseregistrationcode '
|
||||
'WHERE shoppingcart_courseregistrationcode.invoice_id = shoppingcart_invoice.id'
|
||||
}
|
||||
).all()
|
||||
for invoice in invoices:
|
||||
invoice_item = self._create_invoice_item(invoice, orm)
|
||||
orm.CourseRegistrationCode.objects.filter(invoice=invoice).update(invoice_item=invoice_item)
|
||||
|
||||
def backwards(self, orm):
|
||||
"""
|
||||
We don't need the backward migration because the data is already there in old models and
|
||||
schema rollback will automatically remove this new data.
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _create_invoice_item(invoice, orm):
|
||||
unit_price = ((Decimal(invoice.total_amount))/invoice.qty).quantize(Decimal('.01'), rounding=ROUND_DOWN)
|
||||
invoice_item = orm.CourseRegistrationCodeInvoiceItem.objects.create(
|
||||
invoice=invoice,
|
||||
qty=invoice.qty,
|
||||
unit_price=unit_price,
|
||||
course_id=invoice.course_id
|
||||
)
|
||||
return invoice_item
|
||||
|
||||
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, 1, 27, 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, 1, 27, 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.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'}),
|
||||
'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, 1, 27, 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']
|
||||
symmetrical = True
|
||||
@@ -18,7 +18,7 @@ from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.mail import send_mail
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -773,11 +773,11 @@ class OrderItem(TimeStampedModel):
|
||||
self.save()
|
||||
|
||||
|
||||
class Invoice(models.Model):
|
||||
class Invoice(TimeStampedModel):
|
||||
"""
|
||||
This table capture all the information needed to support "invoicing"
|
||||
which is when a user wants to purchase Registration Codes,
|
||||
but will not do so via a Credit Card transaction.
|
||||
This table capture all the information needed to support "invoicing"
|
||||
which is when a user wants to purchase Registration Codes,
|
||||
but will not do so via a Credit Card transaction.
|
||||
"""
|
||||
company_name = models.CharField(max_length=255, db_index=True)
|
||||
company_contact_name = models.CharField(max_length=255)
|
||||
@@ -785,16 +785,39 @@ class Invoice(models.Model):
|
||||
recipient_name = models.CharField(max_length=255)
|
||||
recipient_email = models.CharField(max_length=255)
|
||||
address_line_1 = models.CharField(max_length=255)
|
||||
address_line_2 = models.CharField(max_length=255, null=True)
|
||||
address_line_3 = models.CharField(max_length=255, null=True)
|
||||
address_line_2 = models.CharField(max_length=255, null=True, blank=True)
|
||||
address_line_3 = models.CharField(max_length=255, null=True, blank=True)
|
||||
city = models.CharField(max_length=255, null=True)
|
||||
state = models.CharField(max_length=255, null=True)
|
||||
zip = models.CharField(max_length=15, null=True)
|
||||
country = models.CharField(max_length=64, null=True)
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
|
||||
# This field has been deprecated.
|
||||
# The total amount can now be calculated as the sum
|
||||
# of each invoice item associated with the invoice.
|
||||
# For backwards compatibility, this field is maintained
|
||||
# and written to during invoice creation.
|
||||
total_amount = models.FloatField()
|
||||
internal_reference = models.CharField(max_length=255, null=True)
|
||||
customer_reference_number = models.CharField(max_length=63, null=True)
|
||||
|
||||
# This field has been deprecated in order to support
|
||||
# invoices for items that are not course-related.
|
||||
# Although this field is still maintained for backwards
|
||||
# compatibility, you should use CourseRegistrationCodeInvoiceItem
|
||||
# to look up the course ID for purchased redeem codes.
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
|
||||
internal_reference = models.CharField(
|
||||
max_length=255,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=ugettext_lazy("Internal reference code for this invoice.")
|
||||
)
|
||||
customer_reference_number = models.CharField(
|
||||
max_length=63,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=ugettext_lazy("Customer's reference code for this invoice.")
|
||||
)
|
||||
is_valid = models.BooleanField(default=True)
|
||||
|
||||
def generate_pdf_invoice(self, course, course_price, quantity, sale_price):
|
||||
@@ -824,6 +847,125 @@ class Invoice(models.Model):
|
||||
|
||||
return pdf_buffer
|
||||
|
||||
def __unicode__(self):
|
||||
label = (
|
||||
unicode(self.internal_reference)
|
||||
if self.internal_reference
|
||||
else u"No label"
|
||||
)
|
||||
|
||||
created = (
|
||||
self.created.strftime("%Y-%m-%d") # pylint: disable=no-member
|
||||
if self.created
|
||||
else u"No date"
|
||||
)
|
||||
|
||||
return u"{label} ({date_created})".format(
|
||||
label=label, date_created=created
|
||||
)
|
||||
|
||||
|
||||
INVOICE_TRANSACTION_STATUSES = (
|
||||
# A payment/refund is in process, but money has not yet been transferred
|
||||
('started', 'started'),
|
||||
|
||||
# A payment/refund has completed successfully
|
||||
# This should be set ONLY once money has been successfully exchanged.
|
||||
('completed', 'completed'),
|
||||
|
||||
# A payment/refund was promised, but was cancelled before
|
||||
# money had been transferred. An example would be
|
||||
# cancelling a refund check before the recipient has
|
||||
# a chance to deposit it.
|
||||
('cancelled', 'cancelled')
|
||||
)
|
||||
|
||||
|
||||
class InvoiceTransaction(TimeStampedModel):
|
||||
"""Record payment and refund information for invoices.
|
||||
|
||||
There are two expected use cases:
|
||||
|
||||
1) We send an invoice to someone, and they send us a check.
|
||||
We then manually create an invoice transaction to represent
|
||||
the payment.
|
||||
|
||||
2) We send an invoice to someone, and they pay us. Later, we
|
||||
need to issue a refund for the payment. We manually
|
||||
create a transaction with a negative amount to represent
|
||||
the refund.
|
||||
|
||||
"""
|
||||
invoice = models.ForeignKey(Invoice)
|
||||
amount = models.DecimalField(
|
||||
default=0.0, decimal_places=2, max_digits=30,
|
||||
help_text=ugettext_lazy(
|
||||
"The amount of the transaction. Use positive amounts for payments"
|
||||
" and negative amounts for refunds."
|
||||
)
|
||||
)
|
||||
currency = models.CharField(
|
||||
default="usd",
|
||||
max_length=8,
|
||||
help_text=ugettext_lazy("Lower-case ISO currency codes")
|
||||
)
|
||||
comments = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=ugettext_lazy("Optional: provide additional information for this transaction")
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=32,
|
||||
default='started',
|
||||
choices=INVOICE_TRANSACTION_STATUSES,
|
||||
help_text=ugettext_lazy(
|
||||
"The status of the payment or refund. "
|
||||
"'started' means that payment is expected, but money has not yet been transferred. "
|
||||
"'completed' means that the payment or refund was received. "
|
||||
"'cancelled' means that payment or refund was expected, but was cancelled before money was transferred. "
|
||||
)
|
||||
)
|
||||
created_by = models.ForeignKey(User)
|
||||
last_modified_by = models.ForeignKey(User, related_name='last_modified_by_user')
|
||||
|
||||
|
||||
class InvoiceItem(TimeStampedModel):
|
||||
"""
|
||||
This is the basic interface for invoice items.
|
||||
|
||||
Each invoice item represents a "line" in the invoice.
|
||||
For example, in an invoice for course registration codes,
|
||||
there might be an invoice item representing 10 registration
|
||||
codes for the DemoX course.
|
||||
|
||||
"""
|
||||
objects = InheritanceManager()
|
||||
invoice = models.ForeignKey(Invoice, db_index=True)
|
||||
qty = models.IntegerField(
|
||||
default=1,
|
||||
help_text=ugettext_lazy("The number of items sold.")
|
||||
)
|
||||
unit_price = models.DecimalField(
|
||||
default=0.0,
|
||||
decimal_places=2,
|
||||
max_digits=30,
|
||||
help_text=ugettext_lazy("The price per item sold, including discounts.")
|
||||
)
|
||||
currency = models.CharField(
|
||||
default="usd",
|
||||
max_length=8,
|
||||
help_text=ugettext_lazy("Lower-case ISO currency codes")
|
||||
)
|
||||
|
||||
|
||||
class CourseRegistrationCodeInvoiceItem(InvoiceItem):
|
||||
"""
|
||||
This is an invoice item that represents a payment for
|
||||
a course registration.
|
||||
|
||||
"""
|
||||
course_id = CourseKeyField(max_length=128, db_index=True)
|
||||
|
||||
|
||||
class CourseRegistrationCode(models.Model):
|
||||
"""
|
||||
@@ -835,9 +977,14 @@ class CourseRegistrationCode(models.Model):
|
||||
created_by = models.ForeignKey(User, related_name='created_by_user')
|
||||
created_at = models.DateTimeField(default=datetime.now(pytz.utc))
|
||||
order = models.ForeignKey(Order, db_index=True, null=True, related_name="purchase_order")
|
||||
invoice = models.ForeignKey(Invoice, null=True)
|
||||
mode_slug = models.CharField(max_length=100, null=True)
|
||||
|
||||
# For backwards compatibility, we maintain the FK to "invoice"
|
||||
# In the future, we will remove this in favor of the FK
|
||||
# to "invoice_item" (which can be used to look up the invoice).
|
||||
invoice = models.ForeignKey(Invoice, null=True)
|
||||
invoice_item = models.ForeignKey(CourseRegistrationCodeInvoiceItem, null=True)
|
||||
|
||||
|
||||
class RegistrationCodeRedemption(models.Model):
|
||||
"""
|
||||
@@ -1227,7 +1374,7 @@ class CourseRegCodeItem(OrderItem):
|
||||
# is in another PR (for another feature)
|
||||
from instructor.views.api import save_registration_code
|
||||
for i in range(total_registration_codes): # pylint: disable=unused-variable
|
||||
save_registration_code(self.user, self.course_id, self.mode, invoice=None, order=self.order)
|
||||
save_registration_code(self.user, self.course_id, self.mode, order=self.order)
|
||||
|
||||
log.info("Enrolled {0} in paid course {1}, paid ${2}"
|
||||
.format(self.user.email, self.course_id, self.line_cost)) # pylint: disable=no-member
|
||||
|
||||
@@ -1518,7 +1518,7 @@ class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
|
||||
|
||||
data = {
|
||||
'total_registration_codes': 12, 'company_name': 'Test Group', 'company_contact_name': 'Test@company.com',
|
||||
'company_contact_email': 'Test@company.com', 'sale_price': 122.45, 'recipient_name': 'Test123',
|
||||
'company_contact_email': 'Test@company.com', 'unit_price': 122.45, 'recipient_name': 'Test123',
|
||||
'recipient_email': 'test@123.com', 'address_line_1': 'Portland Street',
|
||||
'address_line_2': '', 'address_line_3': '', 'city': '', 'state': '', 'zip': '', 'country': '',
|
||||
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
|
||||
|
||||
@@ -1664,7 +1664,7 @@ input[name="subject"] {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
li#generate-registration-modal-field-country ~ li#generate-registration-modal-field-total-price,
|
||||
li#generate-registration-modal-field-country ~ li#generate-registration-modal-field-unit-price,
|
||||
li#generate-registration-modal-field-country ~ li#generate-registration-modal-field-internal-reference {
|
||||
@include margin-left(0px !important);
|
||||
@include margin-right(15px !important);
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
var total_registration_codes = $('input[name="total_registration_codes"]').val();
|
||||
var recipient_name = $('input[name="recipient_name"]').val();
|
||||
var recipient_email = $('input[name="recipient_email"]').val();
|
||||
var sale_price = $('input[name="sale_price"]').val();
|
||||
var unit_price = $('input[name="unit_price"]').val();
|
||||
var company_name = $('input[name="company_name"]').val();
|
||||
var company_contact_name = $('input[name="company_contact_name"]').val();
|
||||
var company_contact_email = $('input[name="company_contact_email"]').val();
|
||||
@@ -305,91 +305,91 @@
|
||||
|
||||
if (company_name == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the company name');
|
||||
registration_code_error.text("${_('Please enter the company name')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (($.isNumeric(company_name))) {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the non-numeric value for company name');
|
||||
registration_code_error.text("${_('Please enter the non-numeric value for company name')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (company_contact_name == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the company contact name');
|
||||
registration_code_error.text("${_('Please enter the company contact name')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (($.isNumeric(company_contact_name))) {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the non-numeric value for company contact name');
|
||||
registration_code_error.text("${_('Please enter the non-numeric value for company contact name')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (company_contact_email == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the company contact email');
|
||||
registration_code_error.text("${_('Please enter the company contact email')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (!(validateEmail(company_contact_email))) {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the valid email address');
|
||||
registration_code_error.text("${_('Please enter the valid email address')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (recipient_name == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the recipient name');
|
||||
registration_code_error.text("${_('Please enter the recipient name')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (($.isNumeric(recipient_name))) {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the non-numeric value for recipient name');
|
||||
registration_code_error.text("${_('Please enter the non-numeric value for recipient name')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (recipient_email == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the recipient email');
|
||||
registration_code_error.text("${_('Please enter the recipient email')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (!(validateEmail(recipient_email))) {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the valid email address');
|
||||
registration_code_error.text("${_('Please enter the valid email address')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (address_line == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the billing address');
|
||||
registration_code_error.text("${_('Please enter the billing address')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (sale_price == '') {
|
||||
if (unit_price == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the sale price');
|
||||
registration_code_error.text("${_('Please enter the unit price')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false
|
||||
}
|
||||
if (!($.isNumeric(sale_price))) {
|
||||
if (!($.isNumeric(unit_price))) {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the numeric value for sale price');
|
||||
registration_code_error.text("${_('Please enter the numeric value for unit price')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false
|
||||
}
|
||||
if (total_registration_codes == '') {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the total registration codes');
|
||||
registration_code_error.text("${_('Please enter the number of enrollment codes')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false
|
||||
}
|
||||
if (!($.isNumeric(total_registration_codes))) {
|
||||
registration_code_error.attr('style', 'display: block !important');
|
||||
registration_code_error.text('Please enter the numeric value for total registration codes');
|
||||
registration_code_error.text("${_('Please enter the numeric value for number of enrollment codes')}");
|
||||
generate_registration_button.removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
@@ -600,7 +600,7 @@
|
||||
$('input[name="country"]').val('');
|
||||
$('input[name="customer_reference_number"]').val('');
|
||||
$('input[name="recipient_name"]').val('');
|
||||
$('input[name="sale_price"]').val('');
|
||||
$('input[name="unit_price"]').val('');
|
||||
$('input[name="recipient_email"]').val('');
|
||||
$('input[name="company_contact_name"]').val('');
|
||||
$('input[name="company_contact_email"]').val('');
|
||||
|
||||
@@ -102,12 +102,12 @@
|
||||
</span>
|
||||
</li>
|
||||
<div class="clearfix"></div>
|
||||
<li class="field required text" id="generate-registration-modal-field-total-price">
|
||||
<label for="id_sale_price" class="required text">${_("Sale Price")}</label>
|
||||
<input class="field required" id="id_sale_price" type="text" name="sale_price"
|
||||
<li class="field required text" id="generate-registration-modal-field-unit-price">
|
||||
<label for="id_unit_price" class="required text">${_("Unit Price")}</label>
|
||||
<input class="field required" id="id_unit_price" type="text" name="unit_price"
|
||||
aria-required="true" />
|
||||
<span class="tip-text">
|
||||
${_("The total price for all enrollments purchased")}
|
||||
${_("The price per enrollment purchased")}
|
||||
</span>
|
||||
</li>
|
||||
<li class="field required text" id="generate-registration-modal-field-total-codes">
|
||||
|
||||
Reference in New Issue
Block a user