How to Make a Printable Page with Print CSS in Oracle APEX

Build an invoice page with a Print button, hide the header, menu and buttons on paper with @media print, add a print-only footer and keep totals together.

When users print an Oracle APEX page with Ctrl+P, the browser prints everything on the screen: the header bar, the navigation menu, the buttons, the region shadows and the footer. The result looks like a screenshot, not a document. Universal Theme 26.1 has no print rules of its own, so the page prints exactly as it looks.

Print CSS fixes this. A few rules inside @media print hide the parts of the page that only make sense on screen, remove the decoration and control where pages break. This guide builds an invoice page with a Print button, adds the print rules and tests the printed result on short and long invoices.

The example is an invoice, but the same steps work for any page that users print: an order, a delivery note or a statement. If you need a PDF file created on the server instead, see How to Create PDF Reports in Oracle APEX: A Step-by-Step Tutorial. This article uses only the browser and needs no print server.

Step 1: Create the Tables

Three tables hold the invoices:

  • BILL_CLIENTS has one row per client with the name, address and email.
  • BILL_INVOICES has the invoice header: number, client, dates, tax rate and notes.
  • BILL_INVOICE_LINES has the invoice lines. LINE_AMOUNT is a virtual column, so the amount is always quantity times unit price.

Create the tables with three clients and three invoices. INV-1003 gets 42 lines, so it needs more than one printed page:

Example:

create table bill_clients (
  client_id     number generated by default on null as identity,
  client_name   varchar2(100) not null,
  address_line  varchar2(200),
  city          varchar2(60),
  email         varchar2(120),
  constraint bill_clients_pk primary key (client_id),
  constraint bill_clients_name_uk unique (client_name)
);

create table bill_invoices (
  invoice_id    number generated by default on null as identity,
  invoice_no    varchar2(20) not null,
  client_id     number not null,
  invoice_date  date default on null trunc(sysdate) not null,
  due_date      date not null,
  tax_rate      number(5,2) default on null 18 not null,
  notes         varchar2(400),
  constraint bill_invoices_pk primary key (invoice_id),
  constraint bill_invoices_no_uk unique (invoice_no),
  constraint bill_invoices_client_fk foreign key (client_id)
    references bill_clients (client_id),
  constraint bill_invoices_due_ck check (due_date >= invoice_date),
  constraint bill_invoices_tax_ck check (tax_rate between 0 and 100)
);

create index bill_invoices_client_ix on bill_invoices (client_id);

create table bill_invoice_lines (
  line_id       number generated by default on null as identity,
  invoice_id    number not null,
  line_no       number(4) not null,
  description   varchar2(200) not null,
  quantity      number(10,2) not null,
  unit_price    number(12,2) not null,
  line_amount   number generated always as (round(quantity * unit_price, 2)) virtual,
  constraint bill_invoice_lines_pk primary key (line_id),
  constraint bill_invoice_lines_inv_fk foreign key (invoice_id)
    references bill_invoices (invoice_id) on delete cascade,
  constraint bill_invoice_lines_no_uk unique (invoice_id, line_no),
  constraint bill_invoice_lines_qty_ck check (quantity > 0),
  constraint bill_invoice_lines_price_ck check (unit_price >= 0)
);

insert into bill_clients (client_name, address_line, city, email)
values ('Northwind Traders', '12 Harbour Road', 'Mumbai', 'accounts@northwind.example');
insert into bill_clients (client_name, address_line, city, email)
values ('Blue Lake Hotels', '7 Lake View Street', 'Pune', 'billing@bluelake.example');
insert into bill_clients (client_name, address_line, city, email)
values ('Greenfield School', '45 Park Avenue', 'Delhi', 'office@greenfield.example');

insert into bill_invoices (invoice_no, client_id, invoice_date, due_date, notes)
select 'INV-1001', client_id, date '2026-10-01', date '2026-10-31', 'Payment by bank transfer within 30 days.'
  from bill_clients where client_name = 'Northwind Traders';
insert into bill_invoices (invoice_no, client_id, invoice_date, due_date, tax_rate, notes)
select 'INV-1002', client_id, date '2026-10-05', date '2026-10-20', 5, null
  from bill_clients where client_name = 'Blue Lake Hotels';
insert into bill_invoices (invoice_no, client_id, invoice_date, due_date, notes)
select 'INV-1003', client_id, date '2026-10-10', date '2026-11-09', 'Annual supplies order, delivered in one shipment.'
  from bill_clients where client_name = 'Greenfield School';

insert into bill_invoice_lines (invoice_id, line_no, description, quantity, unit_price)
select i.invoice_id, v.line_no, v.description, v.quantity, v.unit_price
  from bill_invoices i
 cross join (select 1 line_no, 'Laptop stand' description, 4 quantity, 1250 unit_price from dual
             union all select 2, 'Wireless keyboard', 4, 1899 from dual
             union all select 3, 'USB-C docking station', 2, 7450 from dual
             union all select 4, 'Setup and installation (hours)', 3, 900 from dual) v
 where i.invoice_no = 'INV-1001';

insert into bill_invoice_lines (invoice_id, line_no, description, quantity, unit_price)
select i.invoice_id, v.line_no, v.description, v.quantity, v.unit_price
  from bill_invoices i
 cross join (select 1 line_no, 'Room linen set' description, 60 quantity, 820 unit_price from dual
             union all select 2, 'Bath towels', 120, 310 from dual
             union all select 3, 'Laundry service (month)', 1, 15000 from dual) v
 where i.invoice_no = 'INV-1002';

insert into bill_invoice_lines (invoice_id, line_no, description, quantity, unit_price)
select i.invoice_id, n.line_no, 'Notebook pack, grade ' || to_char(mod(n.line_no - 1, 10) + 1) || ', set ' || to_char(n.line_no),
       10 + mod(n.line_no * 7, 25), 45 + mod(n.line_no * 13, 60)
  from bill_invoices i
 cross join (select level as line_no from dual connect by level <= 42) n
 where i.invoice_no = 'INV-1003';

commit;

alter session set nls_date_format = 'YYYY-MM-DD';

select i.invoice_no, c.client_name, i.invoice_date, i.due_date, i.tax_rate,
       count(l.line_id) as lines, sum(l.line_amount) as subtotal
  from bill_invoices i
  join bill_clients c on c.client_id = i.client_id
  left join bill_invoice_lines l on l.invoice_id = i.invoice_id
 group by i.invoice_no, c.client_name, i.invoice_date, i.due_date, i.tax_rate
 order by i.invoice_no;

Output:

Table BILL_CLIENTS created.

Table BILL_INVOICES created.

Index BILL_INVOICES_CLIENT_IX created.

Table BILL_INVOICE_LINES created.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

4 rows inserted.

3 rows inserted.

42 rows inserted.

Commit complete.

Session altered.

INVOICE_NO    CLIENT_NAME          INVOICE_DATE    DUE_DATE         TAX_RATE    LINES    SUBTOTAL
_____________ ____________________ _______________ _____________ ___________ ________ ___________
INV-1001      Northwind Traders    2026-10-01      2026-10-31             18        4       30196
INV-1002      Blue Lake Hotels     2026-10-05      2026-10-20              5        3      101400
INV-1003      Greenfield School    2026-10-10      2026-11-09             18       42       69475
  • BILL_INVOICES_CLIENT_FK and BILL_INVOICE_LINES_INV_FK link the tables. Lines are deleted with their invoice (ON DELETE CASCADE).
  • BILL_INVOICES_DUE_CK stops a due date before the invoice date, and BILL_INVOICES_TAX_CK keeps the tax rate between 0 and 100.
  • INVOICE_DATE and TAX_RATE use DEFAULT ON NULL, so a new invoice gets today's date and 18% tax without a trigger.
  • BILL_INVOICE_LINES_NO_UK stops two lines with the same line number on one invoice.

Step 2: Create a Blank Page

In Page Designer, click Create (the plus icon) and choose Page. Select the Blank Page tile:

Create a Page wizard with the Blank Page tile marked with a red box
Choose the Blank Page type.

Enter Page Number 860 and Name Print Invoice. Keep Page Mode Normal:

Create Blank Page screen with Page Number 860 and Name Print Invoice marked with a red box
Page number and name.

Under Navigation, turn off Use Breadcrumb and set the Icon of the new navigation menu entry to fa-print. Click Create Page:

Navigation section with Use Breadcrumb turned off and Icon fa-print marked with red boxes
No breadcrumb, and a print icon in the menu.

Step 3: Add the Invoice Select List

Right-click Body in the Rendering tree and choose Create Region. Set Name to Choose Invoice, keep Type Static Content and enter no-print in Appearance > CSS Classes:

Choose Invoice region with Name, Type Static Content and CSS Classes no-print marked with red boxes
The no-print class hides this region on paper.

The class does nothing yet. The print CSS in Step 10 hides every element with the class no-print, so the select list and the button never reach the paper.

Right-click the Choose Invoice region, choose Create Page Item and set Name to P860_INVOICE_ID, Type to Select List and Label to Invoice. Under Settings, set Page Action on Selection to Submit Page:

P860_INVOICE_ID with Name, Type Select List, Label Invoice and Page Action on Selection Submit Page marked with red boxes
Choosing an invoice submits the page.

Under List of Values, set Type to SQL Query and Null Display Value to - Select an Invoice -, with this query:

Example:

select i.invoice_no || ' - ' || c.client_name as d,
       i.invoice_id as r
  from bill_invoices i
  join bill_clients c on c.client_id = i.client_id
 order by i.invoice_no
List of Values section with Type SQL Query, the query and Null Display Value marked with a red box
The list shows invoice number and client.

Under Layout, set Column Span to 4, so the button fits next to the field.

Step 4: Add the Print Button and Its Dynamic Action

Right-click the Choose Invoice region and choose Create Button. Set Button Name to PRINT and Label to Print. Under Layout, turn off Start New Row and set Column Span to 2:

PRINT button with Button Name, Label, Region Choose Invoice, Start New Row off and Column Span 2 marked with red boxes
The button sits next to the select list.

Under Appearance, turn on Hot and set Icon to fa-print. Under Behavior, set Action to Defined by Dynamic Action:

Appearance with Hot on and Icon fa-print, and Behavior Action Defined by Dynamic Action marked with red boxes
A dynamic action handles the click.

The button must only show when a real invoice is selected. Under Server-side Condition, set Type to Rows returned with this query:

Example:

select 1
  from bill_invoices
 where invoice_id = to_number(:P860_INVOICE_ID default null on conversion error)
Server-side Condition with Type Rows returned and the invoice query marked with a red box
The button appears only for an existing invoice.

TO_NUMBER with DEFAULT NULL ON CONVERSION ERROR turns any text value into NULL, so the query returns no row instead of raising an error. Step 11 shows why this matters. Every invoice region uses the same condition.

Right-click the PRINT button and choose Create Dynamic Action. Set Name to Print Clicked, Event to Click, Selection Type to Button and Button to PRINT:

Dynamic action Print Clicked with Event Click, Selection Type Button and Button PRINT marked with red boxes
The dynamic action fires on the button click.

Select the True action, set Action to Execute JavaScript Code and enter this code. Keep Fire on Initialization off:

Example:

window.print();
True action with Action Execute JavaScript Code, Code window.print() and Fire on Initialization off marked with red boxes
window.print() opens the browser's Print dialog.

window.print() opens the browser's Print dialog. The button only makes printing easier to find. The print CSS in Step 10 is not tied to the button: it applies whenever the page is printed.

Step 5: Add the Letterhead

Create another region under Body. Set Name to Letterhead, keep Type Static Content and enter this HTML in Source > HTML Code:

Example:

<div class="invoice-letterhead">
  <div>
    <div class="invoice-company">Acme Office Supplies</div>
    <div>21 Market Street, Mumbai 400001</div>
    <div>accounts@acme.example</div>
  </div>
  <div class="invoice-title">INVOICE</div>
</div>
Letterhead region with Name, Type Static Content and the HTML Code marked with red boxes
The company name and the word INVOICE.

Under Appearance, set Template to Blank with Attributes, so the letterhead has no region frame. Set the Server-side Condition to Rows returned with the query from Step 4:

Template Blank with Attributes and Server-side Condition Rows returned with the invoice query marked with red boxes
No frame, and shown only for an existing invoice.

Step 6: Add the Bill To and Invoice Details Regions

Create a region named Bill To and set Type to Classic Report. Set Source > Type to SQL Query, enter the query below and set Page Items to Submit to P860_INVOICE_ID:

Example:

select c.client_name,
       c.address_line,
       c.city,
       c.email
  from bill_invoices i
  join bill_clients c on c.client_id = i.client_id
 where i.invoice_id = :P860_INVOICE_ID
Bill To region with Name, Type Classic Report, SQL Query and Page Items to Submit marked with red boxes
The client of the selected invoice.

Under Layout, set Column Span to 6, so Bill To uses the left half of the page:

Layout section with Column Span 6 marked with a red box
Half of the page width.

Open the Attributes tab. Set Appearance > Template to Value Attribute Pairs - Column, and in Template Options choose Label Width Variable - Medium and Layout Left Aligned Details:

Attributes tab with Template Value Attribute Pairs - Column and Template Options marked with a red box
Each column becomes a label and value line.

Without the Left Aligned Details option, the pairs flowed next to each other across the region in this test. The option puts one label and value on each line.

Create a second classic report region named Invoice Details with this query, Page Items to Submit P860_INVOICE_ID and the same template and template options:

Example:

select invoice_no,
       invoice_date,
       due_date,
       notes
  from bill_invoices
 where invoice_id = :P860_INVOICE_ID

Under Layout, turn off Start New Row and set Column Span to 6, so it sits to the right of Bill To:

Invoice Details Layout with Start New Row off and Column Span 6 marked with red boxes
Invoice Details uses the right half.

In the Columns node of both regions, set clear headings such as Client, Invoice No and Due Date, and the format mask YYYY-MM-DD on the two date columns. Both regions also get the Rows returned condition from Step 4.

Step 7: Add the Items Report

Create a classic report region named Items with this query and Page Items to Submit P860_INVOICE_ID:

Example:

select line_no,
       description,
       quantity,
       unit_price,
       line_amount
  from bill_invoice_lines
 where invoice_id = :P860_INVOICE_ID
 order by line_no
Items region with Name, Type Classic Report, SQL Query and Page Items to Submit marked with red boxes
All lines of the selected invoice.

Open the Attributes tab. Set Layout > Number of Rows to 1000, and in Template Options turn on Stretch Report, set Alternating Rows to Disable and Pagination Display to Hide when all rows displayed:

Items Attributes with Number of Rows 1000 and Template Options marked with red boxes
Show every line of the invoice.

For the number columns, set the alignment to the right and a format mask. Here is LINE_AMOUNT with Heading Amount and Format Mask FM999G999G990D00. QUANTITY uses FM999G990D00:

LINE_AMOUNT column with Heading Amount, right alignment and Format Mask FM999G999G990D00 marked with red boxes
Amounts are right-aligned with two decimals.

Why Number of Rows Matters for Printing

On this page, the Pagination Type of the reports shows No Pagination (Show All Rows). In a first test, I left Number of Rows at its default of 15 and printed INV-1003:

Printed Items table that stops at line 15, with line 15 marked with a red box, followed by totals for all 42 lines
Only 15 of the 42 lines printed, but the totals include all 42.

The report stopped after line 15 and showed no pagination link. The totals, which come from their own query, still added up all 42 lines. A customer would get an invoice whose lines do not add up to the total. Set Number of Rows higher than the longest invoice you expect.

Step 8: Add the Totals

Create a classic report region named Totals. Its query returns one row per total, with a label and a formatted amount:

Example:

with t as (
  select i.tax_rate,
         nvl(sum(l.line_amount), 0) as subtotal
    from bill_invoices i
    left join bill_invoice_lines l on l.invoice_id = i.invoice_id
   where i.invoice_id = :P860_INVOICE_ID
   group by i.tax_rate
),
r as (
  select 1 as seq, 'Subtotal' as label, subtotal as amount from t
  union all
  select 2, 'Tax (' || tax_rate || '%)', round(subtotal * tax_rate / 100, 2) from t
  union all
  select 3, 'Total Due', subtotal + round(subtotal * tax_rate / 100, 2) from t
)
select label,
       to_char(amount, 'FM999G999G990D00') as amount
  from r
 order by seq

Run for invoice 1 (INV-1001) in SQL, the query returns:

Output:

PL/SQL procedure successfully completed.

LABEL        AMOUNT
____________ ____________
Subtotal     30,196.00
Tax (18%)    5,435.28
Total Due    35,631.28
Totals region with Name, Type Classic Report and the totals SQL Query marked with red boxes
Subtotal, tax and total as three rows.

Under Layout, set Column to 8 and Column Span to 5, which places the totals on the right. Enter invoice-totals in Appearance > CSS Classes; the print CSS uses it for a page break rule:

Totals Layout with Column 8 and Column Span 5, and CSS Classes invoice-totals marked with red boxes
Totals on the right with a class for the print CSS.

On the Attributes tab, set Template to Value Attribute Pairs - Row. This template uses the first column as the label and the second as the value. Choose Template Options Label Width Variable - Large and Layout Right Aligned Details:

Totals Attributes with Template Value Attribute Pairs - Row and Template Options marked with a red box
Labels on the left, amounts on the right.

In a first version, the query also returned the sort column SEQ as its first column. The template then showed 1, 2 and 3 as labels and the totals as values, and the amounts disappeared. That is why the query sorts in an inline view and returns only LABEL and AMOUNT.

Step 9: Add a Footer That Only Prints

Create a Static Content region named Invoice Footer with this HTML:

Example:

<p class="invoice-thanks">Thank you for your business. Please quote the invoice number with your payment.</p>
Invoice Footer region with Name, Type Static Content and the HTML Code marked with red boxes
A thank-you line for the paper copy.

Set Template to Blank with Attributes and CSS Classes to print-only:

Appearance with Template Blank with Attributes and CSS Classes print-only marked with a red box
The print-only class hides the footer on screen.

Step 10: Add the Print CSS

Click the page node (Page 860: Print Invoice) at the top of the Rendering tree and paste this into CSS > Inline:

Example:

.print-only {
  display: none;
}
.invoice-letterhead {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  border-bottom: 2px solid #333;
  padding-bottom: 8px;
}
.invoice-company {
  font-size: 20px;
  font-weight: 700;
}
.invoice-title {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: 3px;
}

@media print {
  @page {
    size: A4;
    margin: 12mm;
  }
  .t-Header,
  .t-Body-nav,
  .t-Footer,
  .no-print {
    display: none !important;
  }
  .t-Body-contentInner {
    padding: 0 !important;
  }
  .t-Region {
    border: 0 !important;
    box-shadow: none !important;
  }
  .print-only {
    display: block;
  }
  .t-AVPList-label {
    white-space: normal;
  }
  .t-AVPList-value {
    overflow-wrap: anywhere;
  }
  .invoice-totals {
    break-inside: avoid;
  }
}
Page properties with the print CSS in CSS Inline marked with a red box
The print CSS lives on the page.

The rules before @media print apply on screen: they hide print-only elements and style the letterhead. The rules inside @media print apply only when printing:

  • @page sets A4 paper and a 12 mm margin.
  • .t-Header, .t-Body-nav, .t-Footer and .no-print hide the header bar, the navigation menu, the footer and the Choose Invoice region.
  • .t-Body-contentInner removes the 16 px padding around the page content.
  • .t-Region removes the region borders and shadows.
  • .print-only shows the footer from Step 9.
  • The .t-AVPList rules let long labels and values wrap. Without them, the label Invoice Date was cut to Invoice ... and a long email address ran out of its column on A4 paper.
  • break-inside: avoid on .invoice-totals keeps the totals together on one page.

Step 11: Test the Printed Page

Run the page as any user. Before an invoice is selected, only the Choose Invoice region shows:

Print Invoice page with only the Choose Invoice region and an empty Invoice select list
No invoice regions and no Print button yet.

Select INV-1001 - Northwind Traders. The page submits and shows the invoice with the Print button:

Invoice INV-1001 on screen with letterhead, Bill To, Invoice Details, Items and Totals, and the Print button marked with a red box
The invoice on screen. The footer is hidden here.

Clicking Print opens the browser's Print dialog. In an automated test, a script replaced window.print with a counter, and one click called it exactly once.

The printed pages below were saved as A4 PDF files by headless Chrome, which uses the print media type just like the Print dialog.

Print Without the Print CSS

First, the same invoice printed with the screen rules only, with the navigation menu open:

Printed page 1 without print CSS showing the navigation menu on the left, the header bar, the Choose Invoice region with the Print button and region shadows
Without print CSS: the menu, header and select list are printed.

The navigation menu took 240 px of the page width, the header bar and the Choose Invoice region were printed, and the labels in Invoice Details were cut. The page footer did not fit and went to a second sheet:

Printed page 2 without print CSS containing only the APEX page footer
A second sheet with only the footer.

Print With the Print CSS

Now with the print CSS, also with the navigation menu open:

Printed invoice INV-1001 on one A4 page with letterhead, Bill To, Invoice Details, Items, Totals and the thank-you footer
With print CSS: one clean A4 page.

The invoice fits on one sheet. The menu, header, select list, button and page footer are gone, the regions have no frames, and the thank-you line from Step 9 appears at the bottom.

Print a Long Invoice

INV-1003 has 42 lines. With the print CSS, it printed on three pages:

Page 1 of INV-1003 with letterhead, Bill To, Invoice Details and lines 1 to 17
Page 1: header and the first 17 lines.
Page 2 of INV-1003 with the repeated table header row marked with a red box and lines 18 to 42
Page 2: Chrome repeated the column headings.

Chrome repeated the table header row at the top of page 2 by itself, because the classic report renders a real table with a header section. No line was split between pages.

Page 3 of INV-1003 with the complete Totals block and the thank-you footer
Page 3: the complete totals and the footer.

The totals did not fit below line 42, so the whole block moved to page 3. The next test shows what happens without the break rule.

Print Without the Break Rule

For this test, the rule break-inside: avoid was removed from the page CSS and INV-1003 was printed again:

Page 2 of INV-1003 without the break rule, with the Totals heading and only the Subtotal at the bottom marked with a red box
Without the rule, the totals are split across two pages.

The Totals heading and the subtotal stayed at the bottom of page 2, while the tax and the total due went to page 3. A reader of page 2 sees a subtotal that looks like the amount to pay. With the rule, the three lines always stay together.

Force Invalid Invoice Values

First, as SALES_USER, I added p860_invoice_id=3 to the page URL without a checksum. APEX stopped with Session state protection violation, because Page Access Protection is Arguments Must Have Checksum on this page.

Next, I added a forged option to the select list from the browser console and selected it. APEX accepted the values 999 and abc. In a first version, which used the condition Item is NOT NULL, 999 printed an empty letterhead with No data found in every region, and abc failed with ORA-01722 in all four reports.

With the Rows returned condition from Step 4, both values showed only the Choose Invoice region. Here is the condition query in SQL with a real ID and the two forged values:

Output:

PL/SQL procedure successfully completed.

ITEM_VALUE       ROWS_RETURNED
_____________ ________________
3                            1

PL/SQL procedure successfully completed.

ITEM_VALUE       ROWS_RETURNED
_____________ ________________
999                          0

PL/SQL procedure successfully completed.

ITEM_VALUE       ROWS_RETURNED
_____________ ________________
abc                          0

Only the existing invoice 3 returns a row, so only a real invoice is ever rendered or printed.

Things to Know

  • The stylesheets loaded by a Universal Theme 26.1 page in this example had no print rules for the page layout. The only @media print block was for the rich text editor. Every printable page needs its own print CSS.
  • Use the CSS classes no-print and print-only on any region, item or button to control what appears on paper. The names are your own; they work because of the rules in Step 10.
  • A classic report prints only its Number of Rows, even with Pagination Type No Pagination (Show All Rows). Check this on every report you print.
  • Chrome repeated the table header row on each page and did not split single-line rows without extra CSS. Blocks such as totals need break-inside: avoid.
  • Page Access Protection blocks a changed URL, but not a forged select list value. Region conditions must check that the record exists.

Related Guides

Conclusion

A printable page in Oracle APEX needs a Print button that calls window.print(), CSS classes on the regions that should or should not print, and a short @media print block that hides the theme's header, menu and footer and controls page breaks.

In this example, a short invoice printed on one clean A4 page instead of two, a 42-line invoice repeated its headings and kept its totals together, and forged invoice values printed nothing. The same rules work for orders, delivery notes and any other page users print.

Get more Vinish.dev tutorials on Google

See my articles more often when you search Google.

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