Migration Walkthrough
This exercise takes a small order-line calculation from a SAL outline to PPJ C#, then compares both with the same test cases. It deliberately starts without a database so you can establish language and UI behavior before adding provider and transaction differences.
You need Team Developer for the source baseline, Ice Porter for conversion, Visual Studio with the installed PPJ templates/runtime, and an agreed target from the compatibility matrix. The outline below is teaching material to enter into a small application, not a complete importable .apt file. The C# sample is a complete helper and regression harness; it does not include proprietary PPJ binaries.
1. Define the Behavior
Create an order-line form with numeric fields for quantity, unit price, and discount percentage; a read-only total; and a Calculate button. Use a separate function, TryLineTotal, for the calculation.
The contract is explicit:
- Null input is invalid. Quantity and price must be nonnegative; discount must be between 0 and 100 inclusive.
- An invalid input returns false and sets the receive total to null, clearing any previous successful result.
- Valid input returns true with quantity × price × (100 − discount) / 100.
- This function does not round. A production application's rounding point and rule must come from its business requirements.
2. Run the SAL Baseline
Enter this function in Team Developer. The button calls it, displays the total on success, and displays a validation message on failure. Do not continue displaying an earlier total after a failed call.
Function: TryLineTotal
Returns
Boolean:
Parameters
Number: nQuantity
Number: nUnitPrice
Number: nDiscountPercent
Receive Number: nTotal
Actions
Set nTotal = NUMBER_Null
If nQuantity = NUMBER_Null OR nUnitPrice = NUMBER_Null OR nDiscountPercent = NUMBER_Null
Return FALSE
If nQuantity < 0 OR nUnitPrice < 0 OR nDiscountPercent < 0 OR nDiscountPercent > 100
Return FALSE
Set nTotal = nQuantity * nUnitPrice * (100 - nDiscountPercent) / 100
Return TRUE
Save a copy of the source and the SAL outline. Record the Team Developer version, locale, field formats, and actual results from the cases below. Check both the numeric result and what the form displays; formatting can conceal a numeric difference.
3. Convert and Inspect
Run Ice Porter using the installed release's conversion workflow, selecting the desktop or web target chosen for the exercise. Save the conversion options and log beside the source baseline. Resolve every warning affecting the calculation or form before treating the generated application as equivalent.
Open the generated solution and restore its configured packages. Preserve generated context scopes, event wiring, and receive-parameter declarations. Inspect the output for these translations:
| SAL construct | What to find in the C# output |
|---|---|
| Number / Boolean | PPJ SalNumber / SalBoolean where compatibility requires them. |
| Receive Number | A correctly typed receive parameter, commonly ref SalNumber. |
| NUMBER_Null | PPJ numeric null behavior, distinct from zero. |
| Calculate button action | Generated event/message handling that invokes the function once. |
| Visual method | The generated SalContext scope that preserves window context. |
Exact class placement and generated names depend on the conversion options. The following equivalent, hand-written helper isolates the calculation for testing; it is not a claim that every Ice Porter version emits these exact lines.
using PPJ.Runtime;
public static SalBoolean TryLineTotal(SalNumber quantity, SalNumber unitPrice,
SalNumber discountPercent, ref SalNumber total)
{
total = SalNumber.Null;
if (quantity.IsNull || unitPrice.IsNull || discountPercent.IsNull)
return false;
if (quantity < 0 || unitPrice < 0 || discountPercent < 0 || discountPercent > 100)
return false;
total = quantity * unitPrice * (100 - discountPercent) / 100;
return true;
}
4. Compile and Run the Checks
Build the converted solution with its supported Visual Studio/MSBuild toolchain. Keep the installed PPJ package references; do not substitute an unrelated runtime assembly merely to make compilation succeed.
Download LineTotal.cs, add it to a test project referencing the same PPJ runtime, and call LineTotalSample.RunChecks() from that project's entry point or test method. It throws an exception if a case fails and returns normally if all eleven cases pass. In a console harness, print a success line after the call so a silent process exit is not confused with an unexecuted test.
The included checks initially exercise the equivalent helper. To validate your conversion, adapt the harness call to invoke the actual converted TryLineTotal method and its generated object, then run the form through the same cases. Passing the helper alone does not validate generated code or event wiring.
| Case | Quantity | Price | Discount | Success | Numeric total |
|---|---|---|---|---|---|
| Ordinary | 2 | 12.50 | 0 | Yes | 25 |
| Fractional result | 3 | 19.95 | 10 | Yes | 53.865 |
| Zero quantity | 0 | 12.50 | 0 | Yes | 0 |
| Full discount | 2 | 12.50 | 100 | Yes | 0 |
| Negative quantity | −1 | 12.50 | 0 | No | Null |
| Negative price | 1 | −1 | 0 | No | Null |
| Negative discount | 1 | 12.50 | −1 | No | Null |
| Discount too large | 1 | 12.50 | 101 | No | Null |
| Each input null, separately | One null input | Other values valid | — | No | Null |
Start every failure case with a non-null old total to detect stale receive values. Test a successful button click followed by a failed click. Also test keyboard activation, tab order, decimal separators, and the display format selected in the SAL baseline.
5. Add One Integration at a Time
After this baseline agrees, extend the same small application in this order:
- Load a price through a parameterized query; test no row, a null price, and a provider error. Preserve the SQL context.
- Save an order line in a test database; verify explicit commit and rollback ownership. Exercise a failed operation using the SQL error-flow trace.
- Generate a small order report; compare numeric values, rounding, grouping, and totals using the report lifecycle.
- For Web, run two separate user sessions with different orders and confirm isolation using the operations checks.
Keep expected and actual results, source/build identifiers, screenshots, and defect references together. Accept the baseline only when differences are either fixed or explicitly approved business changes. Archive the original application and the working migrated build before refactoring compatibility code.