Skip to content

Commit ac7209e

Browse files
fix: resolved conflicts
1 parent b9cb840 commit ac7209e

4 files changed

Lines changed: 506 additions & 1 deletion

File tree

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ Unreleased
1717
----------
1818
* nothing unreleased
1919

20+
[7.0.1] - 2026-04-07
21+
---------------------
22+
* feat: add Academy model with product_key, stripe_price_lookup_key, slug, is_active, display_order fields for Essentials academy selection (ENT-11209)
23+
2024
[7.0.0] - 2026-04-07
2125
---------------------
2226
* chore: drop Python 3.11 support

enterprise/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Your project description goes here.
33
"""
44

5-
__version__ = "7.0.0"
5+
__version__ = "7.0.1"
Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
# Generated by Django 5.2.12 on 2026-04-07 11:19
2+
3+
import django.db.models.deletion
4+
import django.utils.timezone
5+
import jsonfield.fields
6+
import model_utils.fields
7+
import simple_history.models
8+
import uuid
9+
from django.conf import settings
10+
from django.db import migrations, models
11+
12+
13+
class Migration(migrations.Migration):
14+
15+
dependencies = [
16+
("enterprise", "0243_add_reminder_sent_at_to_pending_admin"),
17+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
18+
]
19+
20+
operations = [
21+
migrations.CreateModel(
22+
name="EnterpriseAcademy",
23+
fields=[
24+
(
25+
"created",
26+
model_utils.fields.AutoCreatedField(
27+
default=django.utils.timezone.now,
28+
editable=False,
29+
verbose_name="created",
30+
),
31+
),
32+
(
33+
"modified",
34+
model_utils.fields.AutoLastModifiedField(
35+
default=django.utils.timezone.now,
36+
editable=False,
37+
verbose_name="modified",
38+
),
39+
),
40+
(
41+
"uuid",
42+
models.UUIDField(
43+
default=uuid.uuid4,
44+
editable=False,
45+
primary_key=True,
46+
serialize=False,
47+
),
48+
),
49+
(
50+
"name",
51+
models.CharField(
52+
help_text='Short identifier name for the Academy (e.g. "AI Academy").',
53+
max_length=255,
54+
unique=True,
55+
),
56+
),
57+
(
58+
"long_name",
59+
models.CharField(
60+
blank=True,
61+
default="",
62+
help_text="Full display name of the Academy.",
63+
max_length=512,
64+
),
65+
),
66+
(
67+
"description",
68+
models.TextField(
69+
blank=True,
70+
default="",
71+
help_text="Marketing description of the Academy.",
72+
),
73+
),
74+
(
75+
"marketing_url",
76+
models.URLField(
77+
blank=True,
78+
default="",
79+
help_text="Public-facing marketing URL for the Academy landing page.",
80+
max_length=2048,
81+
),
82+
),
83+
(
84+
"thumbnail_url",
85+
models.CharField(
86+
blank=True,
87+
default="",
88+
help_text="Relative S3 path (or absolute URL) for the Academy thumbnail image. If storing a relative path, the S3 bucket base URL is configured via settings.ACADEMY_THUMBNAIL_S3_BASE_URL.",
89+
max_length=2048,
90+
),
91+
),
92+
(
93+
"price",
94+
models.DecimalField(
95+
blank=True,
96+
decimal_places=2,
97+
help_text="Display price for the Academy, synced from Stripe. Stripe (stripe_product_id) is the authoritative source for billing.",
98+
max_digits=10,
99+
null=True,
100+
),
101+
),
102+
(
103+
"tags",
104+
jsonfield.fields.JSONField(
105+
blank=True,
106+
default=list,
107+
help_text="JSON array of tag strings associated with this Academy.",
108+
),
109+
),
110+
(
111+
"stripe_product_id",
112+
models.CharField(
113+
blank=True,
114+
db_index=True,
115+
default="",
116+
help_text="Stripe Product ID for this Academy. Stripe is the single source of truth for pricing and entitlements.",
117+
max_length=255,
118+
),
119+
),
120+
(
121+
"stripe_price_lookup_key",
122+
models.CharField(
123+
blank=True,
124+
db_index=True,
125+
default="",
126+
help_text="Stripe price lookup_key declared in Terraform. Used by the BFF pricing context to resolve prices by academy selection.",
127+
max_length=255,
128+
unique=True,
129+
),
130+
),
131+
(
132+
"edx_catalog_id",
133+
models.UUIDField(
134+
blank=True,
135+
db_index=True,
136+
help_text="UUID of the associated Enterprise Catalog (from the enterprise-catalog service) that defines the content available in this Academy.",
137+
null=True,
138+
),
139+
),
140+
(
141+
"product_key",
142+
models.SlugField(
143+
blank=True,
144+
default="",
145+
help_text="Routing key matching the ?product_key= query param from business.edx.org. Used to identify which academy the user selected at checkout entry.",
146+
max_length=255,
147+
unique=True,
148+
),
149+
),
150+
(
151+
"slug",
152+
models.SlugField(
153+
blank=True,
154+
default="",
155+
help_text="Stable URL-safe slug for academy detail API routes.",
156+
max_length=255,
157+
unique=True,
158+
),
159+
),
160+
(
161+
"is_active",
162+
models.BooleanField(
163+
db_index=True,
164+
default=True,
165+
help_text="Controls whether this academy is visible in public API responses. Set to False to retire an academy without deleting it.",
166+
),
167+
),
168+
(
169+
"display_order",
170+
models.PositiveIntegerField(
171+
db_index=True,
172+
default=0,
173+
help_text="Ascending sort order for academy list API responses.",
174+
),
175+
),
176+
],
177+
options={
178+
"verbose_name": "Academy",
179+
"verbose_name_plural": "Academies",
180+
"ordering": ["display_order", "name"],
181+
},
182+
),
183+
migrations.CreateModel(
184+
name="HistoricalEnterpriseAcademy",
185+
fields=[
186+
(
187+
"created",
188+
model_utils.fields.AutoCreatedField(
189+
default=django.utils.timezone.now,
190+
editable=False,
191+
verbose_name="created",
192+
),
193+
),
194+
(
195+
"modified",
196+
model_utils.fields.AutoLastModifiedField(
197+
default=django.utils.timezone.now,
198+
editable=False,
199+
verbose_name="modified",
200+
),
201+
),
202+
(
203+
"uuid",
204+
models.UUIDField(db_index=True, default=uuid.uuid4, editable=False),
205+
),
206+
(
207+
"name",
208+
models.CharField(
209+
db_index=True,
210+
help_text='Short identifier name for the Academy (e.g. "AI Academy").',
211+
max_length=255,
212+
),
213+
),
214+
(
215+
"long_name",
216+
models.CharField(
217+
blank=True,
218+
default="",
219+
help_text="Full display name of the Academy.",
220+
max_length=512,
221+
),
222+
),
223+
(
224+
"description",
225+
models.TextField(
226+
blank=True,
227+
default="",
228+
help_text="Marketing description of the Academy.",
229+
),
230+
),
231+
(
232+
"marketing_url",
233+
models.URLField(
234+
blank=True,
235+
default="",
236+
help_text="Public-facing marketing URL for the Academy landing page.",
237+
max_length=2048,
238+
),
239+
),
240+
(
241+
"thumbnail_url",
242+
models.CharField(
243+
blank=True,
244+
default="",
245+
help_text="Relative S3 path (or absolute URL) for the Academy thumbnail image. If storing a relative path, the S3 bucket base URL is configured via settings.ACADEMY_THUMBNAIL_S3_BASE_URL.",
246+
max_length=2048,
247+
),
248+
),
249+
(
250+
"price",
251+
models.DecimalField(
252+
blank=True,
253+
decimal_places=2,
254+
help_text="Display price for the Academy, synced from Stripe. Stripe (stripe_product_id) is the authoritative source for billing.",
255+
max_digits=10,
256+
null=True,
257+
),
258+
),
259+
(
260+
"tags",
261+
jsonfield.fields.JSONField(
262+
blank=True,
263+
default=list,
264+
help_text="JSON array of tag strings associated with this Academy.",
265+
),
266+
),
267+
(
268+
"stripe_product_id",
269+
models.CharField(
270+
blank=True,
271+
db_index=True,
272+
default="",
273+
help_text="Stripe Product ID for this Academy. Stripe is the single source of truth for pricing and entitlements.",
274+
max_length=255,
275+
),
276+
),
277+
(
278+
"stripe_price_lookup_key",
279+
models.CharField(
280+
blank=True,
281+
db_index=True,
282+
default="",
283+
help_text="Stripe price lookup_key declared in Terraform. Used by the BFF pricing context to resolve prices by academy selection.",
284+
max_length=255,
285+
),
286+
),
287+
(
288+
"edx_catalog_id",
289+
models.UUIDField(
290+
blank=True,
291+
db_index=True,
292+
help_text="UUID of the associated Enterprise Catalog (from the enterprise-catalog service) that defines the content available in this Academy.",
293+
null=True,
294+
),
295+
),
296+
(
297+
"product_key",
298+
models.SlugField(
299+
blank=True,
300+
default="",
301+
help_text="Routing key matching the ?product_key= query param from business.edx.org. Used to identify which academy the user selected at checkout entry.",
302+
max_length=255,
303+
),
304+
),
305+
(
306+
"slug",
307+
models.SlugField(
308+
blank=True,
309+
default="",
310+
help_text="Stable URL-safe slug for academy detail API routes.",
311+
max_length=255,
312+
),
313+
),
314+
(
315+
"is_active",
316+
models.BooleanField(
317+
db_index=True,
318+
default=True,
319+
help_text="Controls whether this academy is visible in public API responses. Set to False to retire an academy without deleting it.",
320+
),
321+
),
322+
(
323+
"display_order",
324+
models.PositiveIntegerField(
325+
db_index=True,
326+
default=0,
327+
help_text="Ascending sort order for academy list API responses.",
328+
),
329+
),
330+
("history_id", models.AutoField(primary_key=True, serialize=False)),
331+
("history_date", models.DateTimeField()),
332+
("history_change_reason", models.CharField(max_length=100, null=True)),
333+
(
334+
"history_type",
335+
models.CharField(
336+
choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")],
337+
max_length=1,
338+
),
339+
),
340+
(
341+
"history_user",
342+
models.ForeignKey(
343+
null=True,
344+
on_delete=django.db.models.deletion.SET_NULL,
345+
related_name="+",
346+
to=settings.AUTH_USER_MODEL,
347+
),
348+
),
349+
],
350+
options={
351+
"verbose_name": "historical Academy",
352+
"verbose_name_plural": "historical Academies",
353+
"ordering": ("-history_date", "-history_id"),
354+
"get_latest_by": ("history_date", "history_id"),
355+
},
356+
bases=(simple_history.models.HistoricalChanges, models.Model),
357+
),
358+
]

0 commit comments

Comments
 (0)