|
| 1 | + |
| 2 | +B2B Contract Management |
| 3 | +======================= |
| 4 | + |
| 5 | +B2B contracts can be managed via the command line and Django Admin. |
| 6 | + |
| 7 | +.. |
| 8 | +
|
| 9 | + Contracts will also be manageable via Wagtail. |
| 10 | + |
| 11 | + |
| 12 | +The management commands you will need to use are: |
| 13 | + |
| 14 | + |
| 15 | +* ``b2b_contracts`` - manages organizations and contracts |
| 16 | +* ``b2b_list`` - lists data about B2B resources |
| 17 | + |
| 18 | +Quick Setup |
| 19 | +----------- |
| 20 | + |
| 21 | +UAI Scenario: SSO, No Price, Uncapped |
| 22 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 23 | + |
| 24 | +This is how UAI contracts that have SSO integration with the host organization are set up. |
| 25 | + |
| 26 | +**Prerequisites:** You need at least one course to add to your contract. It does not need course runs. |
| 27 | + |
| 28 | + |
| 29 | +#. Create a contract with a new organization: |
| 30 | + ``b2b_contract create UniversityX "UniversityX Contract" sso --description "Test uncapped SSO contract"`` |
| 31 | +#. List the details about your new organization: |
| 32 | + ``b2b_list organizations`` |
| 33 | +#. List the details about your new contract: *(You'll need the ID from the output for the next step.)* |
| 34 | + ``b2b_list contracts`` |
| 35 | +#. Add the course to the contract. This will also create a course run for the course: |
| 36 | + ``b2b_contract courseware <contract ID> <course readable ID>`` |
| 37 | +#. List the details about the contract's courseware: |
| 38 | + ``b2b_list courseware --contract <contract ID>`` |
| 39 | + |
| 40 | +Quick Setup: Non-SSO, No Price, Capped Learner Count |
| 41 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 42 | + |
| 43 | +This is how a B2B contract would be set up typically. |
| 44 | + |
| 45 | +**Prerequisites:** You need at least one course to add to your contract. It does not need course runs. |
| 46 | + |
| 47 | + |
| 48 | +#. Create a contract with a new organization: |
| 49 | + ``b2b_contract create UniversityX "UniversityX Contract" non-sso --max-learners 200 --description "Test capped non-SSO contract"`` |
| 50 | +#. List the details about your new organization: |
| 51 | + ``b2b_list organizations`` |
| 52 | +#. List the details about your new contract: *(You'll need the ID from the output for the next step.)* |
| 53 | + ``b2b_list contracts`` |
| 54 | +#. Add the course to the contract. This will also create a course run for the course: |
| 55 | + ``b2b_contract courseware <contract ID> <course readable ID>`` |
| 56 | +#. List the details about the contract's courseware: |
| 57 | + ``b2b_list courseware --contract <contract ID>`` |
| 58 | +#. Get the enrollment codes for the contract: |
| 59 | + ``b2b_list contracts --codes <contract ID>`` |
| 60 | + The codes will be in a CSV file named ``codes-<contract ID>.csv`` |
| 61 | + |
| 62 | +Quick Setup: User Provisioning |
| 63 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 64 | + |
| 65 | +The easiest way to get users *into* a contract is to just add them to it: from within the Django Admin, navigate to Users, find the user you want to add in, and then add a new relationship using the table at the bottom of the page. |
| 66 | + |
| 67 | +The second easiest way is to set up a contract that involves enrollment codes, and then use the enrollment codes. The output from ``b2b_list contracts --codes`` includes a URL that will add the resulting product to the cart. You then need to use the code and check out. |
| 68 | + |
| 69 | +Contracts and Organizations |
| 70 | +--------------------------- |
| 71 | + |
| 72 | +Contracts are the core data element. They belong to an organization, which provides not much more than some metadata and grouping for the contracts and the learners attached to them. |
| 73 | + |
| 74 | +You can create and manage contracts using the ``b2b_contract create`` and ``b2b_contract modify`` commands. |
| 75 | + |
| 76 | +``b2b_contract create`` |
| 77 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 78 | + |
| 79 | +Creates a contract and optionally its organization. |
| 80 | + |
| 81 | +Syntax: ``b2b_contract create <org name> <contract name> sso|non-sso`` |
| 82 | + |
| 83 | +Options: |
| 84 | + |
| 85 | + |
| 86 | +* ``--start`` / ``--end``\ : Set a start and end date for the contract. These dates will be used when creating the contract's course runs. |
| 87 | +* ``--max-learners``\ : Cap the number of learners that can be attached to the contract. |
| 88 | +* ``--price``\ : Contracts can either allow free enrollment for associated learners, or specify a fixed price for enrollment. If it's the latter, use the ``--price`` option; you will have fixed-price discounts for the learners to use to gain access to the courses. |
| 89 | +* ``--description``\ : A description for the contract. This is a text field and can be as long as necessary. |
| 90 | + |
| 91 | +Notes: |
| 92 | + |
| 93 | + |
| 94 | +* Names should be descriptive. A slug will be automatically generated for the contract and the organization. |
| 95 | +* Specify the organization's name, not an ID. Supply ``--create`` if the organization doesn't already exist - it will be created. Otherwise, the command will look it up based on the name. |
| 96 | + |
| 97 | +Choosing an integration type |
| 98 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 99 | + |
| 100 | +The integration type can be either ``sso`` or ``non-sso``. This has some bearing on how related data elements are set up in the system. |
| 101 | + |
| 102 | +Non-SSO contracts: |
| 103 | + |
| 104 | + |
| 105 | +* Users gain access to the contract (and thus the resources within it) via enrollment codes. |
| 106 | +* Enrollment codes are *always* generated when the contract is modified in any way (other than to deactivate it). |
| 107 | +* There *should* be a learner cap on these contracts. (This is usually the case.) |
| 108 | + |
| 109 | +SSO contracts: |
| 110 | + |
| 111 | + |
| 112 | +* Users can gain access either through enrollment codes *or* by automatic attachment on login, depending on whether or not the contract has specifies a price for the learner or not. |
| 113 | +* Enrollment codes are only generated if the contract specifies a *price* - in that case, the learners need to pay to gain access. |
| 114 | +* Learners can still be capped; we just enforce that cap elsewhere. |
| 115 | + |
| 116 | +SSO contracts require additional setup to be fully functional. The SSO system itself needs to be federated with the host organization's identity provider. This isn't done by the MITx Online code - it requires some work within Keycloak. |
| 117 | + |
| 118 | +``b2b_contract modify`` |
| 119 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 120 | + |
| 121 | +Updates a contract's parameters. |
| 122 | + |
| 123 | +Syntax: ``b2b_contrac modify <contract_id>`` |
| 124 | + |
| 125 | +Options: |
| 126 | + |
| 127 | + |
| 128 | +* ``--start`` / ``--end``\ : Set a start and end date for the contract. These dates will be used when creating the contract's course runs. |
| 129 | +* ``--max-learners``\ : Cap the number of learners that can be attached to the contract. |
| 130 | +* ``--price``\ : Contracts can either allow free enrollment for associated learners, or specify a fixed price for enrollment. If it's the latter, use the ``--price`` option; you will have fixed-price discounts for the learners to use to gain access to the courses. |
| 131 | +* ``--active / [--inactive|--delete]`` - Activate or deactivate the contract. |
| 132 | +* ``--no-price / --no-learner-cap / --no-start-date / --no-end-date`` - Remove price, learner cap, start/end dates. |
| 133 | + |
| 134 | +Notes: |
| 135 | + |
| 136 | + |
| 137 | +* If you remove the price or learner caps from the contract, any unused enrollment codes should be adjusted. Note that removing the price essentially means "set it to $0" - the enrollment codes will be set to fixed price $0 discounts. |
| 138 | +* If you remove the start and end date, any existing course runs will not be modified. |
| 139 | + |
| 140 | +Courseware |
| 141 | +---------- |
| 142 | + |
| 143 | +Once a contract is created, it needs resources to provide to the learners. Use ``b2b_contract courseware`` for this. |
| 144 | + |
| 145 | +``b2b_contract courseware`` |
| 146 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 147 | + |
| 148 | +Add or remove courseware from the contract. (This means either programs or courses, but it could mean more types in the future.) |
| 149 | + |
| 150 | +Syntax: ``b2b_contract courseware <contract_id> <courseware_readable_id>`` |
| 151 | + |
| 152 | +Options: |
| 153 | + |
| 154 | + |
| 155 | +* ``--no-create-runs`` - don't create runs. You probably don't want this, but it can be useful if you want to create just a course shell for content folks to use, for example. |
| 156 | +* ``--remove`` - Remove the course from the contract. This *will* unset the existing course runs - they won't be deleted, but they also won't be associated with the contract anymore. |
| 157 | + |
| 158 | +Notes: |
| 159 | + |
| 160 | + |
| 161 | +* The courseware ID is the readable ID. Don't specify a course run. |
| 162 | +* If you specify a program, it will create runs for all the courses within the program. You may need to re-run this if the program is modified. |
| 163 | + |
| 164 | +Listing Data |
| 165 | +------------ |
| 166 | + |
| 167 | +The ``b2b_list`` command contains a handful of subcommands for listing out the data within the B2B system. Some of this you can get through the Django Admin and/or Wagtail admin but this is more of a one-stop-shop for this data. |
| 168 | + |
| 169 | +``b2b_list organizations`` |
| 170 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 171 | + |
| 172 | +Lists out the orgs in the system. |
| 173 | + |
| 174 | +Syntax: ``b2b_list organizations`` |
| 175 | + |
| 176 | +Options: |
| 177 | + |
| 178 | + |
| 179 | +* ``--org / --organization`` - only show the specified contract ID |
| 180 | + |
| 181 | +Notes: |
| 182 | + |
| 183 | +This is pretty basic. It's just for verifying the base data about the org. |
| 184 | + |
| 185 | +``b2b_list contracts`` |
| 186 | +^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 187 | + |
| 188 | +Lists out the contracts within the system. |
| 189 | + |
| 190 | +Syntax: ``b2b_list contracts`` |
| 191 | + |
| 192 | +Options: |
| 193 | + |
| 194 | + |
| 195 | +* ``--org / --organization`` - Limit the output to contracts for the specified organization ID. |
| 196 | + |
| 197 | +Notes: |
| 198 | + |
| 199 | +This is also pretty basic, but you'll need the contract ID to add courseware. |
| 200 | + |
| 201 | +``b2b_list courses --codes`` |
| 202 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 203 | + |
| 204 | +Lists the enrollment codes in the contract. |
| 205 | + |
| 206 | +Syntax: ``b2b_list courses --codes <id>`` where ID is the contract ID |
| 207 | + |
| 208 | +Options: |
| 209 | + |
| 210 | + |
| 211 | +* ``--codes-out <filename>`` - Write the codes to a file using this name. Otherwise, the output file is ``codes-<id>.csv``. |
| 212 | + |
| 213 | +Notes: |
| 214 | + |
| 215 | + |
| 216 | +* This is sort of a separate command from the base ``contracts`` command, but also not really. |
| 217 | +* The resulting CSV file contains 4 fields: |
| 218 | + |
| 219 | + * Code: the code itself. This is a UUID. |
| 220 | + * Product: the product that the code can be used for. |
| 221 | + * Redeemed: whether or not the code has been redeemed. |
| 222 | + * Product URL: the URL to distribute to the learner; this will start a new basket for them with just the product in it, so they can apply and use the code. |
| 223 | + |
| 224 | +Enrollment Codes and Products |
| 225 | +----------------------------- |
| 226 | + |
| 227 | +When courseware items are attached to contracts, a few things happen: |
| 228 | + |
| 229 | + |
| 230 | +* Runs are created. At this point, this is exclusively course runs. |
| 231 | + |
| 232 | + * If a standalone course is added, then it's one run for the course. |
| 233 | + * If a program is added, then a course run is created for each course that's listed in the program's requirements. |
| 234 | + * In all cases, the run's dates are set to either mirror the contract, or set with start dates in the past so learners can get into the course. The courses are marked live and self-paced. If you need to modify this afterwards, you can do so through the normal methods. |
| 235 | + * Notably, CMS pages *are not* created for these. They will not be accessible through the front end. But they should show up on dashboards for learners who are enrolled in the course. |
| 236 | + |
| 237 | +* Products are created for each run. |
| 238 | + |
| 239 | + * If the contract specifies no price, these are set to $0. |
| 240 | + * If the contract does specify a price, these are set to the price in the contract. |
| 241 | + |
| 242 | +* Discount codes are created, if necessary. |
| 243 | + |
| 244 | + * These are enrollment codes - enrollment codes are discount codes. |
| 245 | + * Codes are created and linked to the individual products that were created for the contract. |
| 246 | + * If there's *no learner cap*\ , there is *one* code per product, set to unlimited use. |
| 247 | + * If there's a learner cap, there is one code *per learner* per product, set to one-time use. (That means if the cap is set to 200 learners, and the contract specified 20 courses, there will be 4,000 created codes.) |
| 248 | + * In either case, the codes are set to Fixed Price with the price specified in the contract ($0 or more), payment type Sales, and the is bulk flag is set to True. |
| 249 | + |
| 250 | +When the contract is updated, or courseware is attached to the contract, the system will update or create codes in the system: |
| 251 | + |
| 252 | + |
| 253 | +* If the modifications mean that the cap is removed, the integration type is SSO, and the price is set to zero, then all unredeemed codes are deleted. This is the only scenario in which the system will remove codes. |
| 254 | +* Otherwise: |
| 255 | + |
| 256 | + * If the learner cap is removed, the redemption type changes from One Time to Unlimited. |
| 257 | + * If the price is adjusted, the price in the discount code is also adjusted. |
| 258 | + * If new courses are added, and as such new products are created, new discount codes are also created. |
| 259 | + * If the learner cap is *changed* (increased or decreased), codes are created or deactivated accordingly. |
| 260 | + |
| 261 | +This adjustment happens any time the contract is saved or courseware is added. This is potentially an expensive operation, so it's queued. |
| 262 | + |
| 263 | +User Provisioning / New Rules for Checkout |
| 264 | +------------------------------------------ |
| 265 | + |
| 266 | +In a lot of cases, learners will be provisioned based on what's in their Keycloak profile, because they'll log in using federated SSO with their host organization. The system will note this and then attach the user to the appropriate contract when they get back into MITx Online. |
| 267 | + |
| 268 | +In a lot of *other* cases, learners will have to use an enrollment code. This works using the ecommerce system. Learners use the special URL that adds the course to their basket, apply the provided code, and then check out. This enrolls them in the course (and collects any fee that may be required). |
| 269 | + |
| 270 | +Most of the time, a B2B contract will be set up to allow the learner to access resources for free. The ecommerce system additionally usually just processes zero-value baskets without further input from the learner. This is a problem for B2B contracts, since we need them to use the enrollment code. So, some changes have been made to the checkout process. If the basket is zero value *but* the product is linked to a B2B run, then we *do* present them with the basket screen. They won't get a checkout button until they've applied the code (which was a happy accident). Once they've applied it and hit the checkout button, the system will then notice it's a zero-value cart, and complete the process; they won't have to do a round-trip through CyberSource. |
| 271 | + |
| 272 | +If the contract specifies a price, then the learner will arrive at the Cart page as normal. The process is otherwise identical to the process they'd take if they were buying a regular upgrade. |
| 273 | + |
| 274 | +We additionally have added more checks to discount codes and products in the cart. Specifically, if the learner has a product linked to a B2B course, they are *not allowed* to check out without applying the correct discount code. The system will kick them back to the cart page with the invalid discount message. |
| 275 | + |
| 276 | +Manual Management |
| 277 | +^^^^^^^^^^^^^^^^^ |
| 278 | + |
| 279 | +In the Django Admin, we've added a new inline to User to display the contracts the learner belongs to. You can add or remove associations here. At present, we don't expose this in management commands. |
| 280 | + |
| 281 | +If you need to provision a user *quickly*\ , this is the easiest way to do it, as long as the user already exists. |
| 282 | + |
| 283 | +Other Management |
| 284 | +---------------- |
| 285 | + |
| 286 | +The plan is to allow for orgs and contracts to be managed through the Wagtail interface. To that end, organizations and contracts are really Wagtail pages. |
| 287 | + |
| 288 | +If you run ``configure_wagtail``\ , you will see a new top-level Organizations page under Home Page. You should be able to create and see organizations in there, and you should be able to create contracts for those organizations from there too. *If you need to manually edit a contract or org* you should do it using the Wagtail interface, if you can't do it for whatever reason from the command line. |
| 289 | + |
| 290 | +The Wagtail interface currently does not allow you to manage courseware assignments or retrieve the list of enrollment codes, so this is not a complete interface. |
| 291 | + |
| 292 | +We also expose the contracts and pages via the Django Admin. You should not use this for editing - it is there for convenience, so you can view the data without having to hunt around in Wagtail for it. This interface may be removed in future. |
0 commit comments