How to Show Totals and Calculated Columns in an Interactive Grid in Oracle APEX

A virtual column for the amount, a Set Value dynamic action in the browser, an IG aggregate in the footer, and a live total that updates before you save.

Order, invoice and quote screens almost always need numbers that come from other numbers. Each line has an amount built from quantity, price and discount, and the screen needs a total of all lines. Users expect both to change the moment they type, not only after they save.

Oracle APEX covers most of this declaratively. This guide stores the line amount as a virtual column, recalculates it in the browser with a Set Value dynamic action, shows a Sum in the grid footer with an IG aggregate, and adds a small live total for the rows that are not saved yet.

The older guide How to Validate Interactive Grid Rows Before Saving in Oracle APEX checks grid rows and stops bad data. This one calculates: a column computed by the database, an amount computed in the browser, and totals that stay correct before and after saving.

Step 1: Create the Tables

The products come from the PRODUCTS table of the earlier guides. If you start from scratch, create it with a few rows:

Example:

create table products (
    product_id number generated by default as identity
               constraint products_pk primary key,
    name       varchar2(60)  not null,
    category   varchar2(30)  not null,
    price      number(10, 2) not null
               constraint products_price_ck check (price >= 0)
);

insert into products (name, category, price) values ('Laptop Pro 14', 'Computers', 1450);
insert into products (name, category, price) values ('Laptop Air 13', 'Computers', 990);
insert into products (name, category, price) values ('Monitor 27', 'Displays', 320);
insert into products (name, category, price) values ('Monitor 32', 'Displays', 480);
insert into products (name, category, price) values ('Keyboard', 'Accessories', 45);
insert into products (name, category, price) values ('Mouse', 'Accessories', 25);
insert into products (name, category, price) values ('Docking Station', 'Accessories', 180);
insert into products (name, category, price) values ('Headset', 'Audio', 95);
commit;

Now create QUOTES and QUOTE_LINES, a view QUOTE_TOTALS_V with the total of each quote, and two sample quotes:

Example:

create table quotes (
    quote_id      number generated by default as identity
                  constraint quotes_pk primary key,
    customer_name varchar2(100) not null,
    quote_date    date default on null sysdate
);

create table quote_lines (
    line_id      number generated by default as identity
                 constraint quote_lines_pk primary key,
    quote_id     number not null
                 constraint quote_lines_quote_fk references quotes on delete cascade,
    product_id   number not null
                 constraint quote_lines_product_fk references products,
    quantity     number(6) not null
                 constraint quote_lines_qty_ck check (quantity > 0),
    unit_price   number(10, 2) not null
                 constraint quote_lines_price_ck check (unit_price >= 0),
    discount_pct number(5, 2) default on null 0
                 constraint quote_lines_disc_ck check (discount_pct between 0 and 100),
    line_amount  number(12, 2) generated always as
                 (round(quantity * unit_price * (1 - discount_pct / 100), 2)) virtual,
    constraint quote_lines_product_uk unique (quote_id, product_id)
);

create index quote_lines_product_ix on quote_lines (product_id);

create or replace view quote_totals_v as
select q.quote_id,
       q.customer_name,
       count(l.line_id)              as line_count,
       nvl(sum(l.line_amount), 0)    as total_amount
from   quotes q
left join quote_lines l on l.quote_id = q.quote_id
group by q.quote_id, q.customer_name;

insert into quotes (customer_name) values ('Northwind Traders');
insert into quotes (customer_name) values ('Blue Harbor Hotels');

insert into quote_lines (quote_id, product_id, quantity, unit_price, discount_pct)
select 1, product_id, 2, price, 0 from products where name = 'Laptop Pro 14';
insert into quote_lines (quote_id, product_id, quantity, unit_price, discount_pct)
select 1, product_id, 2, price, 10 from products where name = 'Monitor 27';
insert into quote_lines (quote_id, product_id, quantity, unit_price, discount_pct)
select 1, product_id, 5, price, 0 from products where name = 'Mouse';
insert into quote_lines (quote_id, product_id, quantity, unit_price, discount_pct)
select 2, product_id, 10, price, 5 from products where name = 'Headset';
commit;

select l.line_id, l.quote_id, p.name as product, l.quantity,
       l.unit_price, l.discount_pct, l.line_amount
from   quote_lines l
join   products p on p.product_id = l.product_id
order by l.line_id;

select quote_id, customer_name, line_count, total_amount
from   quote_totals_v
order by quote_id;

Output:

Table QUOTES created.

Table QUOTE_LINES created.

Index QUOTE_LINES_PRODUCT_IX created.

View QUOTE_TOTALS_V created.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

Commit complete.

   LINE_ID    QUOTE_ID PRODUCT             QUANTITY    UNIT_PRICE    DISCOUNT_PCT    LINE_AMOUNT
__________ ___________ ________________ ___________ _____________ _______________ ______________
         1           1 Laptop Pro 14              2          1450               0           2900
         2           1 Monitor 27                 2           320              10            576
         3           1 Mouse                      5            25               0            125
         4           2 Headset                   10            95               5          902.5

   QUOTE_ID CUSTOMER_NAME            LINE_COUNT    TOTAL_AMOUNT
___________ _____________________ _____________ _______________
          1 Northwind Traders                 3            3601
          2 Blue Harbor Hotels                1           902.5
  • LINE_AMOUNT is a virtual column. The database calculates it from QUANTITY, UNIT_PRICE and DISCOUNT_PCT whenever the row is read, so the stored amount can never disagree with the line.
  • QUOTE_TOTALS_V adds up the line amounts per quote. The total is not stored anywhere, so no trigger has to keep it up to date.
  • DISCOUNT_PCT uses DEFAULT ON NULL 0, and the check constraints keep quantity, price and discount in range.
  • The unique key QUOTE_LINES_PRODUCT_UK starts with QUOTE_ID, so it also serves the foreign key on QUOTE_ID. The index QUOTE_LINES_PRODUCT_IX covers the foreign key on PRODUCT_ID.

Step 2: Create the Interactive Grid Page

In App Builder, open the application and click Create Page. On the Component tab, click Interactive Grid:

Create a Page wizard with the Interactive Grid tile marked with a red box
The Interactive Grid tile.

Enter Page Number 260 and Name Quote Lines. Keep Data Source Local Database and Source Type Table, enter QUOTE_LINES as Table / View Name, and turn on Editing Enabled:

Create Interactive Grid wizard with Page Number 260, Name Quote Lines, Table / View Name QUOTE_LINES and Editing Enabled on, marked with red boxes
An editable grid on QUOTE_LINES.

Scroll down to Navigation. Turn off Use Breadcrumb, keep Use Navigation on with Create a new entry, and set Icon to fa-calculator. Click Next:

Create Interactive Grid wizard Navigation section with Use Breadcrumb off, Use Navigation on and Icon fa-calculator marked with red boxes
A menu entry for the page.

Primary Key Column 1 is already LINE_ID (Number). Click Create Page:

Create Interactive Grid wizard with Primary Key Column 1 LINE_ID (Number) marked with a red box
The primary key of the grid.

The wizard creates the region Quote Lines with one column per table column, and the process Quote Lines - Save Interactive Grid Data. It shows all lines of all quotes, and LINE_AMOUNT is an editable Number Field. The next steps change both.

Step 3: Add the Quote Selector and the Total Items

In Page Designer, right-click Body and choose Create Region. Set Name to Quote and Type to Static Content. Set Sequence to 10, so the region sits above the grid:

Page Designer showing the region Quote with Type Static Content and Sequence 10 marked with red boxes
A region for the quote selector and the totals.

Right-click the Quote region and choose Create Page Item. Name it P260_QUOTE_ID, set Type to Select List and Label to Quote. In the Settings group, set Page Action on Selection to Submit Page:

Page Designer showing P260_QUOTE_ID with Type Select List, Label Quote and Page Action on Selection Submit Page marked with red boxes
Choosing a quote reloads the page for that quote.

In the List of Values group, set Type to SQL Query and enter this query. Set Null Display Value to - Select Quote -:

Example:

select 'Quote ' || quote_id || ' - ' || customer_name as display_value,
       quote_id as return_value
from   quotes
order by quote_id
Page Designer showing the List of Values of P260_QUOTE_ID with Type SQL Query, the query and Null Display Value - Select Quote - marked with red boxes
The list of quotes.

Create a second item in the Quote region: P260_CURRENT_TOTAL, Type Display Only, Label Current Total. Turn off Send On Page Submit, and in the Layout group turn off Start New Row, so the item sits next to the select list:

Page Designer showing P260_CURRENT_TOTAL with Type Display Only, Label Current Total, Send On Page Submit off and Start New Row off marked with red boxes
The live total, filled by JavaScript in Step 10.

Create a third item, P260_SAVED_TOTAL, the same way with Label Saved Total. In its Source group, set Type to SQL Query (return single value), enter this query, and set Used to Always, replacing any existing value in session state:

Example:

select to_char(total_amount, 'FM999G999G990D00')
from   quote_totals_v
where  quote_id = :P260_QUOTE_ID
Page Designer showing the Source group of P260_SAVED_TOTAL with Type SQL Query, the query on QUOTE_TOTALS_V and Used Always marked with red boxes
The total that the database has saved.

In a first test, the item had Format Mask 999G999G990D00 and a query that returned the plain number. The page still showed 3601 without separators, so the query now formats the value with TO_CHAR.

Step 4: Show Only the Lines of the Selected Quote

Select the Quote Lines region. In the Source group, enter this Where Clause and set Page Items to Submit to P260_QUOTE_ID:

Example:

quote_id = :P260_QUOTE_ID
Page Designer showing the Quote Lines region with Where Clause quote_id = :P260_QUOTE_ID and Page Items to Submit P260_QUOTE_ID marked with red boxes
The grid loads the lines of one quote.

In the Advanced group of the same region, set HTML DOM ID to quote-lines. The JavaScript of Step 10 finds the grid by this ID:

Page Designer showing the Advanced group of the Quote Lines region with HTML DOM ID quote-lines marked with a red box
A fixed ID for the grid region.

Expand Quote Lines > Columns and select QUOTE_ID. Set Type to Hidden:

Page Designer showing the QUOTE_ID column with Type Hidden marked with a red box
Users never type the quote ID.

In the Default group, set Type to Item and Item to P260_QUOTE_ID. A new row then gets the selected quote:

Page Designer showing the Default group of QUOTE_ID with Type Item and Item P260_QUOTE_ID marked with a red box
New lines belong to the selected quote.

Step 5: Set Up the Editable Columns

Select PRODUCT_ID. Set Type to Select List and Heading to Product:

Page Designer showing the PRODUCT_ID column with Type Select List and Heading Product marked with red boxes
Users pick a product by name.

In the List of Values group, set Type to SQL Query, enter this query, and set Null Display Value to - Select Product -:

Example:

select name as display_value,
       product_id as return_value
from   products
order by name
Page Designer showing the List of Values of PRODUCT_ID with Type SQL Query, the query and Null Display Value - Select Product - marked with red boxes
The product list.

Select QUANTITY. In the Settings group, set Minimum Value to 1. The grid then refuses 0 and negative numbers before the row is sent to the database:

Page Designer showing the Settings group of QUANTITY with Minimum Value 1 marked with a red box
The same rule as QUOTE_LINES_QTY_CK.

The JavaScript of Step 8 reads the column values with $v("QUANTITY"). In a first test, $v("QUANTITY") returned an empty string. Grid column items get generated IDs such as C31813497886340839, not the column name. In the Advanced group, set HTML DOM ID to QUANTITY:

Page Designer showing the Advanced group of QUANTITY with HTML DOM ID QUANTITY marked with a red box
The column item can now be read as QUANTITY.

Select UNIT_PRICE and set HTML DOM ID to UNIT_PRICE in the same way:

Page Designer showing the Advanced group of UNIT_PRICE with HTML DOM ID UNIT_PRICE marked with a red box
The price column item.

Select DISCOUNT_PCT. Set Heading to Discount %, Minimum Value to 0 and Maximum Value to 100:

Page Designer showing DISCOUNT_PCT with Heading Discount %, Minimum Value 0 and Maximum Value 100 marked with red boxes
The same range as QUOTE_LINES_DISC_CK.

In the Default group, set Type to Static and Static Value to 0. A new row then starts with no discount, and the amount formula has a number to work with:

Page Designer showing the Default group of DISCOUNT_PCT with Type Static and Static Value 0 marked with a red box
New rows start with 0 percent.

Set HTML DOM ID to DISCOUNT_PCT as well:

Page Designer showing the Advanced group of DISCOUNT_PCT with HTML DOM ID DISCOUNT_PCT marked with a red box
The discount column item.

Step 6: Make Amount a Query Only Column

The wizard made LINE_AMOUNT an editable column that the save process writes. In a first test, changing a quantity and clicking Save failed with this message:

Output:

ORA-54017: UPDATE operation disallowed on virtual columns

Select LINE_AMOUNT. Keep Type Number Field, set Heading to Amount, and in the Appearance group set Format Mask to FM999G999G990D00:

Page Designer showing LINE_AMOUNT with Heading Amount and Format Mask FM999G999G990D00 marked with red boxes
Amounts with two decimals and group separators.

Turn off Value Required in the Validation group, and turn on Query Only in the Source group. The save process then leaves LINE_AMOUNT out of INSERT and UPDATE, and the database calculates it:

Page Designer showing LINE_AMOUNT with Value Required off and Query Only on marked with red boxes
The grid shows the amount but never saves it.

In the Advanced group, set HTML DOM ID to LINE_AMOUNT and Custom Attributes to readonly tabindex="-1". Users cannot type into the cell, and Tab skips it:

Page Designer showing the Advanced group of LINE_AMOUNT with HTML DOM ID LINE_AMOUNT and Custom Attributes readonly tabindex=-1 marked with red boxes
The amount is read-only in the browser.

Why not Display Only or Read Only? Both were tested first. With Type Display Only, the browser console showed Set value not allowed for field, and the amount did not change. Read Only Type Always gave the same error. The grid model refuses new values for such columns, so the readonly HTML attribute is used instead.

Step 7: Fill the Unit Price from the Product

Open the Dynamic Actions tab, right-click Events and choose Create Dynamic Action. Name it Get product price. In the When group, set Event to Change, Selection Type to Column(s), Interactive Grid to Quote Lines and Column(s) to PRODUCT_ID:

Page Designer showing the dynamic action Get product price with Event Change, Selection Type Column(s), Interactive Grid Quote Lines and Column(s) PRODUCT_ID marked with red boxes
The event fires when the product of a row changes.

Select the True action and set Action to Set Value. Set Set Type to SQL Statement, enter this query, and set Items to Submit to PRODUCT_ID. In Affected Elements, set Selection Type to Column(s) and Column(s) to UNIT_PRICE:

Example:

select price
from   products
where  product_id = :PRODUCT_ID
Page Designer showing the Set Value action with Set Type SQL Statement, the query, Items to Submit PRODUCT_ID and Affected Elements Column(s) UNIT_PRICE marked with red boxes
The price of the chosen product goes into the row.

Items to Submit PRODUCT_ID sends the column value of the current row, so :PRODUCT_ID in the query holds that value. Suppress Change Event stays off: the new price fires a change on UNIT_PRICE, and that change starts the next dynamic action.

Step 8: Calculate the Amount in the Browser

Create a second dynamic action named Calculate amount. Set Event to Change, Selection Type to Column(s), Interactive Grid to Quote Lines, and Column(s) to QUANTITY, UNIT_PRICE and DISCOUNT_PCT:

Page Designer showing the dynamic action Calculate amount with Event Change, Selection Type Column(s), Interactive Grid Quote Lines and Column(s) QUANTITY,UNIT_PRICE,DISCOUNT_PCT marked with red boxes
Any of the three columns starts the calculation.

Set the True action to Set Value with Set Type JavaScript Expression and this expression. In Affected Elements, set Selection Type to Column(s) and Column(s) to LINE_AMOUNT:

Example:

apex.locale.formatNumber(
    Math.round($v("QUANTITY") * $v("UNIT_PRICE") * (100 - $v("DISCOUNT_PCT"))) / 100,
    "FM999G999G990D00"
)
Page Designer showing the Set Value action with Set Type JavaScript Expression, the expression and Affected Elements Column(s) LINE_AMOUNT marked with red boxes
The browser repeats the formula of the virtual column.

Quantity times price times (100 minus discount) gives the amount in cents, Math.round rounds it, and the division by 100 brings it back. This is the same rounding as ROUND(..., 2) in the virtual column, so the browser and the database agree.

Leave Suppress Change Event off. In a first test it was on: the column item got 200.00, but the grid model and the visible cell kept the old 125.00. The grid copies a column item into the model only on its change event.

Step 9: Add the Sum to the Grid Footer

An aggregate is part of the grid's saved report, so it is set at runtime. Run the page as a developer from App Builder, choose Quote 1, and open Actions > Data > Aggregate:

Quote Lines grid with the Actions menu open and Data, Aggregate marked with red boxes
The Aggregate dialog is under Actions, Data.

Set Column to Amount and Aggregation to Sum, and click Save:

Aggregation dialog with Column Amount and Aggregation Sum marked with a red box
A Sum of the Amount column.

The grid now shows an Overall Sum row under the lines. To keep it for every user, open Actions > Report > Save. This menu entry is enabled only when you run the page with a developer session:

Actions menu with Report and Save marked with red boxes, and the Overall Sum row of 3,601.00 under the grid lines
Save the primary report with the aggregate.

APEX confirms with Default report saved for all users:

Success message Default report saved for all users above the Quote Lines grid with the Sum (Amount) setting and the Overall Sum row
The aggregate is now part of the primary report.

The aggregate is calculated by the database with the grid query. In a test, the quantity of Mouse was changed from 5 to 8. The Amount cell showed 200.00 at once, but Overall Sum stayed at 3,601.00. After Save it still showed 3,601.00 until the page was reloaded. Steps 10 and 11 handle both cases.

Step 10: Show a Live Total of the Unsaved Rows

The grid model in the browser holds every row, including changed, new and deleted ones. A small function adds up the amounts in the model and writes the result into P260_CURRENT_TOTAL. Select the page (Page 260: Quote Lines) and open the JavaScript group:

Page Designer showing the page JavaScript group with Function and Global Variable Declaration and Execute when Page Loads filled, marked with red boxes
The live total code of the page.

Enter this code in Function and Global Variable Declaration:

Example:

function showCurrentTotal() {
    var model = apex.region("quote-lines").call("getViews", "grid").model,
        total = 0;
    model.forEach(function (record, index, id) {
        var meta = model.getRecordMetadata(id);
        if (!meta.deleted && !meta.agg) {
            total += apex.locale.toNumber(model.getValue(record, "LINE_AMOUNT")) || 0;
        }
    });
    $s("P260_CURRENT_TOTAL", apex.locale.formatNumber(total, "FM999G999G990D00"));
}

And this code in Execute when Page Loads:

Example:

apex.region("quote-lines").call("getViews", "grid").model.subscribe({
    onChange: showCurrentTotal
});
showCurrentTotal();

quote-lines is the HTML DOM ID set in Step 4. The function skips rows marked as deleted, and it skips the aggregate row, which the model also holds (meta.agg). apex.locale.toNumber reads amounts like 2,900.00 back as numbers.

subscribe with onChange calls the function on every model change: a new value, a new row, a deleted row, or a refresh. This is the only JavaScript code in the guide that a declarative setting could not replace.

Step 11: Refresh the Totals After Save

Create a dynamic action named Refresh after save. Set Event to Save [Interactive Grid], Selection Type to Region, and Region to Quote Lines:

Page Designer showing the dynamic action Refresh after save with Event Save [Interactive Grid], Selection Type Region and Region Quote Lines marked with red boxes
The event fires after the grid has saved its changes.

Set the True action to Refresh with Selection Type Region and Region Quote Lines. The refresh runs the grid query again, so the aggregate is calculated again:

Page Designer showing the Refresh action with Affected Elements Selection Type Region and Region Quote Lines marked with red boxes
A fresh query gives a fresh Overall Sum.

Right-click True and add a second action: Set Value with Set Type SQL Statement, Items to Submit P260_QUOTE_ID, and Affected Elements Item(s) P260_SAVED_TOTAL. Use the same query as the item source:

Example:

select to_char(total_amount, 'FM999G999G990D00')
from   quote_totals_v
where  quote_id = :P260_QUOTE_ID
Page Designer showing the Set Value action with Set Type SQL Statement, the query on QUOTE_TOTALS_V, Items to Submit P260_QUOTE_ID and Affected Elements Item(s) P260_SAVED_TOTAL marked with red boxes
The saved total is read again from the view.

Click Save in Page Designer.

Step 12: Test It

ORDER_EDITOR opened Quote Lines and chose Quote 1 - Northwind Traders. All three totals showed 3,601.00:

Quote Lines page with Current Total 3,601.00 and Saved Total 3,601.00 marked with a red box and the Overall Sum row 3,601.00 marked with a red box
Before any change, the totals agree.

The quantity of Mouse was changed from 5 to 8. Amount changed to 200.00 and Current Total to 3,676.00 before anything was saved. Overall Sum and Saved Total still showed the saved state, 3,601.00:

Grid row Mouse with Quantity 8 and Amount 200.00 marked with red boxes, Current Total 3,676.00 marked with a red box, and Overall Sum still 3,601.00 marked with a red box
The browser calculates the row and the live total.

Save was clicked. APEX showed Changes saved, the grid was refreshed, and Overall Sum and Saved Total moved to 3,676.00:

Message Changes saved, Saved Total 3,676.00 marked with a red box and Overall Sum 3,676.00 marked with a red box
After saving, all three totals agree again.

Next, Add Row inserted a new line and Keyboard was chosen. The Get product price action filled Unit Price with 45. After quantity 4, Amount showed 180.00 and Current Total 3,856.00:

New grid row Keyboard with Unit Price 45 and Amount 180.00 marked with red boxes, and Current Total 3,856.00 marked with a red box
The price comes from the product, the amount from the formula.

After Save, the database held these rows:

Example:

select l.line_id, p.name as product, l.quantity, l.unit_price,
       l.discount_pct, l.line_amount
from   quote_lines l
join   products p on p.product_id = l.product_id
where  l.quote_id = 1
order by l.line_id;

select quote_id, customer_name, line_count, total_amount
from   quote_totals_v
order by quote_id;

Output:

   LINE_ID PRODUCT             QUANTITY    UNIT_PRICE    DISCOUNT_PCT    LINE_AMOUNT
__________ ________________ ___________ _____________ _______________ ______________
         1 Laptop Pro 14              2          1450               0           2900
         2 Monitor 27                 2           320              10            576
         3 Mouse                      8            25               0            200
        22 Keyboard                   4            45               0            180

   QUOTE_ID CUSTOMER_NAME            LINE_COUNT    TOTAL_AMOUNT
___________ _____________________ _____________ _______________
          1 Northwind Traders                 4            3856
          2 Blue Harbor Hotels                1           902.5

The view returns 3856 for Quote 1, the same as the page showed.

Then a wrong value: the discount of Monitor 27 was set to 150. The Minimum and Maximum Value settings marked the cell, and Save stopped with Correct errors before saving. The cell showed this message:

Grid cell Discount % with value 150 marked with a red box and the message Discount % must be a number between 0 and 100.
The range is checked in the browser.

Note that the browser formula still ran, so Amount showed -320.00 and Current Total dropped until the value was fixed. In a first test without Minimum and Maximum Value, the same input reached the database and failed with ORA-02290 on QUOTE_LINES_DISC_CK.

Last, a forced amount. In the browser console, the model value of LINE_AMOUNT for Laptop Pro 14 was set to 1.00, and Save was clicked. APEX showed Changes saved, but after the refresh the row showed 2,900.00 again. Query Only kept the value out of the UPDATE, and the database calculated the amount.

The database rules also hold for any other program:

Example:

insert into quote_lines (quote_id, product_id, quantity, unit_price, discount_pct)
select 2, product_id, 1, price, 150 from products where name = 'Mouse';

update quote_lines set line_amount = 1 where line_id = 1;

insert into quote_lines (quote_id, product_id, quantity, unit_price)
select 2, product_id, 3, price from products where name = 'Mouse';

select line_id, quantity, unit_price, discount_pct, line_amount
from   quote_lines
where  quote_id = 2
order by line_id;

rollback;

Output:

Error starting at line : 1 File @ b28/02-checks.sql
In command -
insert into quote_lines (quote_id, product_id, quantity, unit_price, discount_pct)
select 2, product_id, 1, price, 150 from products where name = 'Mouse'
Error report -
ORA-02290: check constraint (APXSOL.QUOTE_LINES_DISC_CK) violated
Help: https://docs.oracle.com/error-help/db/ora-02290/

Error starting at line : 4 File @ b28/02-checks.sql
In command -
update quote_lines set line_amount = 1 where line_id = 1
Error at Command Line : 4 Column : 24 File @ b28/02-checks.sql
Error report -
SQL Error: ORA-54017: UPDATE operation disallowed on virtual columns
Help: https://docs.oracle.com/error-help/db/ora-54017/

1 row inserted.

   LINE_ID    QUANTITY    UNIT_PRICE    DISCOUNT_PCT    LINE_AMOUNT
__________ ___________ _____________ _______________ ______________
         4          10            95               5          902.5
        24           3            25               0             75

Rollback complete.

The insert with discount 150 fails on the check constraint, the update of LINE_AMOUNT fails with ORA-54017, and the insert without a discount gets 0 from DEFAULT ON NULL and an amount of 75.

Things to Know

  • An IG aggregate is calculated by the database with the grid query. It does not follow unsaved edits, and a save alone does not update it. Refresh the region after Save [Interactive Grid].
  • Set Value can only change a grid column that the model accepts values for. Display Only and Read Only columns fail with Set value not allowed for field.
  • Keep Suppress Change Event off when Set Value targets a grid column. With it on, the model kept the old value in this example.
  • Give grid columns an HTML DOM ID when JavaScript reads them with $v. Without it, the column items have generated IDs.
  • The browser formula is for display only. Query Only and the virtual column make sure the saved amount always comes from the database.
  • The live total is a browser figure. The Saved Total from QUOTE_TOTALS_V is the one to trust in reports and other programs.

Related Guides

Conclusion

To show calculated columns and totals in an Interactive Grid in Oracle APEX, let the database own the numbers. A virtual column calculates each line amount, a view calculates the quote total, and the grid column is Query Only, so no trigger and no save code is needed.

On the page, a Set Value dynamic action repeats the formula as users type, an IG aggregate shows the saved Sum in the footer, and a short model subscription shows the live total before saving. A refresh after Save brings all totals back in line.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted