HATEOAS

Why giving "Agency" to your cheapest assets is the ultimate proof of the Inverted AI architecture.

Labels
AI(19) AJAX(112) App Studio(10) Apple(1) Application Builder(245) Application Factory(207) ASP.NET(95) ASP.NET 3.5(45) ASP.NET Code Generator(72) ASP.NET Membership(28) Azure(18) Barcode(2) Barcodes(3) BLOB(18) Business Rules(3) Business Rules/Logic(140) BYOD(13) Caching(2) Calendar(5) Charts(29) Cloud(14) Cloud On Time(2) Cloud On Time for Windows 7(2) Code Generator(54) Collaboration(11) command line(1) Conflict Detection(1) Content Management System(12) COT Tools for Excel(26) CRUD(1) Custom Actions(1) Data Aquarium Framework(122) Data Sheet(9) Data Sources(22) Database Lookups(50) Deployment(22) Designer(178) Device(1) Digital Workforce(3) DotNetNuke(12) EASE(20) Email(6) Features(101) Firebird(1) Form Builder(14) Globalization and Localization(6) HATEOAS(10) How To(1) Hypermedia(3) Inline Editing(1) Installation(5) JavaScript(20) Kiosk(1) Low Code(3) Mac(1) Many-To-Many(4) Maps(6) Master/Detail(36) Micro Ontology(5) Microservices(4) Mobile(63) Mode Builder(3) Model Builder(3) MySQL(10) Native Apps(5) News(18) OAuth(9) OAuth Scopes(1) OAuth2(14) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(11) PKCE(2) Postgre SQL(1) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(186) Reports(48) REST(29) RESTful(33) RESTful Workshop(14) RFID tags(1) SaaS(7) Security(81) SharePoint(12) SPA(5) SQL Anywhere(3) SQL Server(26) SSO(1) Stored Procedure(4) Teamwork(15) Tips and Tricks(87) Tools for Excel(3) Touch UI(93) Transactions(5) Tutorials(183) Universal Windows Platform(3) User Interface(337) Video Tutorial(37) Web 2.0(100) Web App Generator(101) Web Application Generator(607) Web Form Builder(40) Web.Config(9) Workflow(28)
Archive
Blog
HATEOAS
Wednesday, February 4, 2026PrintSubscribe
Is It Crazy to Give a Soap Dispenser an AI Concierge?

At first glance, the idea sounds like the punchline to a bad Silicon Valley joke.

“We put an LLM in the restroom soap dispenser.”

It evokes images of over-engineered, $500 smart-home gadgets that require firmware updates just to wash your hands. But at Code On Time, we argue that giving a soap dispenser its own AI Concierge isn't a gimmick. In fact, it is the logical endgame of the Inverted AI architecture—and it might be the smartest operational investment a business can make.

Here is why "The Janitor-Bot" is the future of enterprise automation.

image1.png

The Problem: Paying Humans to "Walk"

In Facility Management, the biggest line item on the P&L is labor. But what are you actually paying for?

If a janitor is paid $20/hour to patrol a building and check 50 restrooms, and only 5 of them actually need supplies, you have effectively paid for 90% waste. You paid a human to walk past 45 perfectly full dispensers just to verify they were full.

Traditional IoT (Internet of Things) tried to solve this with "Dashboards." The sensor sends data to a screen. A red light blinks. But dashboards are passive. If the manager is busy, the red light is ignored, the soap runs out, and the customer experience fails.

The Solution: Asset Agency

The Inverted AI approach changes the equation. We don't want the soap dispenser to just log data. We want it to act.

By utilizing the Device Authorization Flow built into every Code On Time application, we can treat a $10 microcontroller (like a Raspberry Pi Pico) as a secure user.

  1. The Janitor logs into the app on their phone.
  2. They generate a code and enter it into the dispenser.
  3. The dispenser is now authenticated as a "Digital Co-Worker" acting on behalf of that janitor.

Now, the device doesn't just update a database row. It sends a Prompt to the AI Concierge via the universal “chat” endpoint.

While we might imagine the device chatting in plain English, the actual communication is much less informal. To ensure precision, the microcontroller transmits a structured JSON payload representing the "ground truth" of its physical state:

JSON
123456{
  "device_id": "Restroom-94-Dispenser",
  "sensor_value": 0.10,
  "battery_level": "good",
  "timestamp": "2026-02-04T14:30:00Z"
}

The AI Concierge ingests this raw data, combines it with the server-side context (e.g., “It’s only 2 PM and usage is already 90%”), and translates the structured signal into a clear intent:

Device Prompt: “Soap level is at 10%. Usage rate is high today.”

AI Concierge (Server-Side): “Acknowledged. Checking inventory… Stock is available in Closet B. creating Task #902 for Janitor Steve: ‘Refill Restroom 4 immediately.’ Sending SMS alert.”

The dispenser didn't just flash a light. It managed the workflow. It checked inventory, assigned a task, and notified the human—all without a manager getting involved.

The Economics of "Inverted AI"

Why is this feasible now? Because we have flipped the architecture.

In Anthropic's exciting "Computer Use" vision of AI, you need a massive model to visually "look" at screens. That is too expensive for a janitor, let alone a bathroom appliance.

But in the Code On Time Inverted AI:

  • The Hardware is Dumb: A $14 sensor.
  • The Interface is Cheap: Tiny JSON text packets.
  • The Intelligence is Centralized: The "Brain" lives in your Code On Time app, running on a scalable server (and soon, .NET Core containers).

Because the marginal cost of processing a text prompt is fractions of a penny, it creates a new economic reality: Intelligence is now cheap enough to deploy everywhere.

The "Sovereign" Thing

This architecture turns your physical assets into Sovereign Agents.

  • They carry an Identity (via OAuth).
  • They respect Permissions (they can't order supplies if they aren't allowed).
  • They speak Natural Language (or at least, structured prompts that the LLM understands).

When a soap dispenser can truthfully report its state, authenticate securely, and trigger business logic, it stops being a piece of plastic. It becomes a member of the workforce—one that never sleeps, never forgets, and costs almost nothing to employ.

The Verdict

So, is it crazy to give a soap dispenser an AI Concierge?

If you think AI is only for writing poems or code, then yes, it's crazy.

But if you see AI as the ultimate Friction Remover for business operations, it is inevitable.

We are building a future where your business data, your human employees, and your physical infrastructure all inhabit the same secure, conversational mesh. And it starts with a simple prompt: "I need a refill."

Labels: AI, HATEOAS, OAuth2, RESTful
Thursday, January 29, 2026PrintSubscribe
The "Four Nines" Fallacy: Why 90% AI Accuracy is an Enterprise Failure

In the world of mission-critical infrastructure, we have a sacred metric: The Four Nines.

Whether it’s a database, a payment gateway, or a cloud provider, 99.99% reliability is the minimum threshold for professional trust. It translates to less than an hour of downtime per year. We don’t accept a bank that loses one out of every ten transfers, nor a restaurant where the kitchen "hallucinates" the wrong order 10% of the time.

Yet, in the current AI gold rush, we are being told that 90% accuracy is a triumph.

image1.png

A recent research paper from Amazon on Insight Agents—a sophisticated multi-agent system designed to help sellers talk to their business data—proudly cites a 90% success rate based on human evaluation. While this is an impressive feat of linguistic inference, from an operational standpoint, 90% is a catastrophic failure. In the enterprise, if an agent isn't hitting "Four Nines," it isn't a worker; it's a liability.

The Probabilistic Trap: When "Inference" Isn't "Operation"

The gap between 90% and 99.99% isn't just a matter of "more training data." It is a fundamental architectural divide.

Most modern AI agents are built on a probabilistic model. They use LLMs as "Planners" that try to reason their way through a problem, generate a sequence of steps, and perhaps even write some SQL along the way.

  • The Problem: LLMs are poets, not accountants. They are designed to predict the next most likely token, not to maintain the integrity of a transaction.
  • The Result: Even with high-quality inference, these agents lack the proper security context and deterministic guardrails. They operate in a "God-mode" sandbox where they can imagine actions that shouldn't exist or bypass business rules because they "reasoned" it was the right thing to do.

If your restaurant's automated ordering system has a 90% accuracy rate, you don't have an innovation—you have a kitchen nightmare.

Where the Money Is: Steering vs. Rowing

To understand why this failure rate is so dangerous, we have to look at where value is actually created in the enterprise.

  • Inference Helps Steer (The Compass): AI inference is fantastic at "steering." It can analyze sentiment, summarize reports, or suggest a new marketing strategy. If the AI is 90% accurate here, a human "Captain" can easily spot the error. This is high-value strategic insight.
  • Operations Impact the Bottom Line (The Engine): This is where the "rowing" happens. This is the AI actually executing a refund, moving inventory, or updating a sensitive customer record.

In Operations, there is no room for a "likely" answer. A transaction is either valid or it is a bug. When an AI "Digital Co-Worker" operates your business at machine speed, any percentage of error is magnified. You don't get rich by having an AI that can discuss your business; you get rich by having an AI that can operate it with total reliability.

The Inversion: AI as the "Super-User"

How do we close the 10% gap? We stop treating the AI as a Developer and start treating it as an Expert User.

Instead of asking the AI to "plan" its own path (which leads to hallucinations), we build a Maze—a deterministic data graph—and ask the AI to navigate it. At Code On Time, we do this using REST Level 3 HATEOAS (Hypermedia as the Engine of Application State).

  1. The Maze is Human-Made: The developer defines the business rules. If an invoice is "Paid," the server physically does not render a "Delete" link. The AI cannot "hallucinate" a deletion because the button doesn't exist in its universe.
  2. The AI is a Super-Hacker: The AI "surfs" this data graph at machine speed. It doesn't need to know the database schema; it just needs to pick the most likely "link" from a pre-vetted list of valid actions.
  3. The Responsibility is Human: If the AI makes a "wrong" move, it’s because the developer drew the maze wrong. This moves the problem from the "unfixable" realm of LLM weights to the "fixable" realm of application logic.

Reclaiming the Throne

By "rigging the game" this way, we reclaim 99.99% operational certainty.

The AI provides the speed and the quality of decision-making (the Inference), but the Digital Co-Worker architecture provides the safety and the security (the Operation). The AI acts as your Human Alter-Ego, managing access_token and refresh_token through a secure public address loopback, ensuring it can never exceed the authority you’ve granted it.

The real prize of AI integration isn't a smarter chatbot; it's a Digital Co-Worker that lets you deploy a workforce that is faster than a human, but just as grounded in your business rules.

In the enterprise, we don't need AI to be "creative" with our data. We need it to be perfect. And with the right architecture, perfection is finally a deterministic outcome.
Labels: AI, HATEOAS
Wednesday, January 28, 2026PrintSubscribe
The $63 Million "PDF Killer" vs. The Digital Co-Worker: How We Replaced the Report Viewer

The tech world is currently buzzing about Factify, a startup that just raised $63 million to "kill the PDF." Their premise is sound: the PDF is a digital fossil—a static, "dumb" snapshot of data that cannot be queried, verified, or easily updated. They want to replace it with a live, interactive document format.

At Code On Time, we understand this frustration better than most. But we believe the solution isn't to reinvent the file format. The solution is to reinvent the worker that creates it.

image2.png
In fact, the architecture of our Digital Co-Worker wasn't originally designed to compete with AI agents. It was born from a much more pragmatic necessity: we needed to replace the retired Microsoft Report Viewer.

The Legacy: The Era of the .rdlc

For years, the gold standard for enterprise reporting in the .NET ecosystem was the Microsoft Report Viewer. If you have built applications with Code On Time, you know the drill. You would open Visual Studio, meticulously design an .rdlc file, and deploy it.

Under the hood, our framework did the heavy lifting. We had a robust engine (which still powers thousands of apps today) that would take an incoming request, hydrate a DataTable from your Data Controller, and force-feed it into the report definition to spit out a PDF.

It looked something like this:

C#
12345678910111213141516171819// The "Old Guard" of Report Generation
void IHttpHandler.ProcessRequest(HttpContext context)
{
    // 1. Decrypt the request
    var request = JsonConvert.DeserializeObject<PageRequest>(q);
    
    // 2. Execute the Data Controller (The "Business Logic")
    var page = ControllerFactory.CreateDataController().GetPage(request.Controller, request.View, request);
    var table = page.ToDataTable();

    // 3. Load the hard-coded .rdlc definition
    var reportTemplate = Controller.CreateReportInstance(null, templateName, ...);

    // 4. Render the PDF
    var report = Render(request, table, reportTemplate, "Pdf");
    
    // 5. Stream it to the user
    context.Response.OutputStream.Write(report.Data, 0, report.Data.Length);
}

This code works. It is reliable. But it is rigid. The "intelligence" is frozen in the compiled code and the static .rdlc layout. If a customer wanted a slightly different invoice format, or if they wanted to summarize the data differently, a developer had to open Visual Studio and redeploy the app.

When Microsoft effectively retired the Report Viewer technology, we faced a choice: Do we build another rigid reporting engine, or do we leap forward?

The Shift: From "Report Viewer" to "Digital Co-Worker"

We realized that the problem wasn't the PDF format itself. The PDF is fine as a final output. The problem was the process of generating it.

We didn't need a better renderer; we needed an entity that could understand the data and compose the document, just like a human employee would.

This led to the birth of the Digital Co-Worker.

Instead of a static .rdlc file, the Digital Co-Worker uses .ai files—canned prompts that describe the task. And instead of binding tightly to a raw DataTable, it interacts with our Level 3 HATEOAS API.

How It Works: The "Factify" Implementation

While Factify is trying to build a "smart document" infrastructure from scratch, Code On Time developers already have the infrastructure to generate smart documents today.

  1. The Prompt (.ai): You define a "canned" prompt. For example: "Generate an invoice for Order #1024 using the 'Professional' HTML template."
  2. The Agent: The Digital Co-Worker picks up this task. It doesn't query the database directly (which bypasses security). It uses the HATEOAS API to "surf" your data, respecting all access control rules, just like the old ProcessRequest method did, but dynamically.
  3. The Merge: The Co-Worker takes the structured JSON data it retrieved and merges it into a standard user-provided HTML template.
  4. The Output: The result is a perfect PDF, an HTML email, or even a text summary.

The Future is "Canned" Intelligence

The next generation of Code On Time will lean heavily into this model. We are enabling the use of "canned" prompts in .ai files that act as the new "Report Definitions."

Imagine a scenario where your application needs to send a dunning letter to a customer.

  • Old Way: You design a specific .rdlc report for "Overdue Notices."
  • New Way: You have a .ai file with a prompt: "Review the customer's payment history. If they are >60 days overdue, generate a polite but firm PDF letter using the 'Urgent' template and email it to the billing contact."

The Digital Co-Worker does the work. It "factifies" the data by pulling it from the source of truth (the app), processing it through the prompt, and stamping it into a PDF.

Here is the new section titled "Practical Implementation: The .ai Report". You can insert this into the blog post draft immediately preceding the Conclusion.

Practical Implementation: The .ai Report

How does this work in the real world? In the legacy era, a developer would spend hours in Visual Studio designing a layout. In the Digital Co-Worker era, they simply write a text file.

Let's look at a concrete example: report1.ai.

This file acts as the "Source Code" for the report. However, unlike a script, it is not executed line-by-line by a compiler. It is interpreted by the Digital Co-Worker's State Machine.

The "State to Keep" Architecture

When the Digital Co-Worker wakes up to generate this report, it does not browse the public internet. It is hermetically sealed within your application's security perimeter.

It looks at two things to decide what to do next:

  1. The HATEOAS Resource: The current JSON response from your application (the "page" it is currently on).
  2. The State Array (state_to_keep): A memory log that persists across server requests.

The state_to_keep array initializes with the content of your .ai file as the first item:

["Goal: Generate Inventory Report (report1.ai)", "<universal system instruction for LLM how to navigate>", ...]

The Co-Worker reads the prompt in the first slot, realizes it needs product data, examines the current HATEOAS links, and "clicks" the transition to the data source (e.g., the products endpoint). Once the data is loaded into its context, it executes the final generation step.

The Prompt (report1.ai)

Here is the actual prompt used to generate the report. Notice how it defines logic (calculate averages), styling (zebra stripes), and behavior (print-to-pdf) in natural language.

text
12345678910111213141516171819202122232425Act as a Front-End Developer and Data Analyst. Create a standalone HTML5/CSS3 file that generates a Product Inventory Report based on a hardcoded JSON array.

1. Data & Logic Requirements:
   Dataset: Use a collection array containing: Product Name, Category, Supplier, and Unit Price.
   Exclusions: Do not display any unique IDs or internal metadata.
   Calculation: Write a JavaScript function to calculate the average unitPrice of all items in the array.
   Conditional Formatting: Automatically apply a specific CSS class to any unitPrice that is above the calculated average, rendering that specific text in Bold Red.

2. Visual & UI Design:
   Typography: Use a clean, professional sans-serif font stack (e.g., Segoe UI or Roboto).
   Layout: Use a wide table layout with a clear visual hierarchy.
   Styling: 
   * Include Zebra-striping (alternating row colors) for readability.
   * Right-align all currency values to ensure decimal alignment.
   * Create a professional header with a border-bottom and a summary bar displaying the calculated average.
   * Add a 'Total' footer row at the bottom of the table with a distinct background color.

3. Functionality & Print Specs:
   Print Button: Include a 'Print to PDF' button at the top of the report.
   Print CSS: Use @media print rules to:
   * Hide the 'Print' button during printing.
   * Ensure background colors (zebra stripes) and red text highlights are preserved using -webkit-print-color-adjust: exact;.
   * Self-Contained: All CSS must be in a <style> tag and all JS in a <script> tag within a single HTML file.

Take the data for the report at https://demo.codeontime.com/v2/public/products

Note: The final line in the prompt above (Take the data for the report at…) is included here only so you can test this prompt yourself in ChatGPT or Gemini. This line is not needed when the prompt is sent to the Digital Co-Worker.

In a real deployment, the Digital Co-Worker navigates the data as an alter-ego of the current user. It does not need a public URL; it autonomously traverses your application's secure HATEOAS API, collects the necessary data records based on the user's permissions, and injects them directly into the context. The Co-Worker then performs the logic and produces the final HTML output on its own.

The Result

The Co-Worker produces a single, self-contained HTML string. It executes the Javascript to calculate averages on the fly and renders a clean, print-ready interface.

The user doesn't see a "file download." They see a standard HTML page rendered instantly in their browser. When they click Print to PDF, the browser's native engine handles the serialization, preserving the "Red Bold" high-value items exactly as requested.

This is the definition of "Factify" realized: Live data + Intelligent Logic + Standard Output. No proprietary viewers required.

image1.png

This is the visualization of prompt output produced by the Fast Gemini.

image3.png

This is the visualization of prompt output produced by the Chat GPT.

This is the script generated by Fast Gemini to implement the report.

JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Product Inventory Report</title>
    <style>
        /* 2. Visual & UI Design */
        :root {
            --primary-color: #2c3e50;
            --accent-color: #f8f9fa;
            --border-color: #dee2e6;
            --highlight-color: #d0021b;
            --zebra-color: #f2f2f2;
            --footer-bg: #e9ecef;
        }

        body {
            font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            color: var(--primary-color);
            line-height: 1.6;
            margin: 40px;
            background-color: #fff;
        }

        .report-container {
            max-width: 1000px;
            margin: 0 auto;
        }

        header {
            border-bottom: 3px solid var(--primary-color);
            margin-bottom: 20px;
            padding-bottom: 10px;
            display: flex;
            justify-content: space-between;
            align-items: flex-end;
        }

        h1 { margin: 0; font-weight: 600; }

        .summary-bar {
            background: var(--accent-color);
            padding: 15px;
            border-radius: 4px;
            margin-bottom: 20px;
            border: 1px solid var(--border-color);
            font-weight: bold;
        }

        /* Table Styling */
        table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 30px;
        }

        th {
            background-color: #fff;
            text-align: left;
            padding: 12px;
            border-bottom: 2px solid var(--primary-color);
            text-transform: uppercase;
            font-size: 0.85rem;
            letter-spacing: 1px;
        }

        td {
            padding: 10px 12px;
            border-bottom: 1px solid var(--border-color);
        }

        /* Zebra-striping */
        tbody tr:nth-child(even) {
            background-color: var(--zebra-color);
        }

        /* Alignment and Formatting */
        .text-right { text-align: right; }
        
        /* Conditional Formatting Class */
        .above-average {
            color: var(--highlight-color);
            font-weight: bold;
        }

        .footer-row {
            background-color: var(--footer-bg) !important;
            font-weight: bold;
        }

        /* Button Styling */
        #print-btn {
            background-color: var(--primary-color);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            margin-bottom: 20px;
            font-size: 14px;
            transition: opacity 0.2s;
        }

        #print-btn:hover { opacity: 0.9; }

        /* 3. Print Specs */
        @media print {
            #print-btn { display: none; }
            body { margin: 0; }
            .report-container { width: 100%; max-width: none; }
            
            * {
                -webkit-print-color-adjust: exact !important;
                print-color-adjust: exact !important;
            }
        }
    </style>
</head>
<body>

    <div class="report-container">
        <button id="print-btn" onclick="window.print()">Print to PDF</button>

        <header>
            <h1>Product Inventory Report</h1>
            <div id="report-date"></div>
        </header>

        <div class="summary-bar" id="summary-bar">
            Calculating Average...
        </div>

        <table id="inventory-table">
            <thead>
                <tr>
                    <th>Product Name</th>
                    <th>Category</th>
                    <th>Supplier</th>
                    <th class="text-right">Unit Price</th>
                </tr>
            </thead>
            <tbody id="inventory-body">
                </tbody>
            <tfoot>
                <tr class="footer-row" id="footer-row">
                    <td colspan="3">Total Items</td>
                    <td id="total-count" class="text-right">0</td>
                </tr>
            </tfoot>
        </table>
    </div>

    <script>
        // 1. Data Requirements (Sampled from the requested demo link)
        const inventoryData = [
            { ProductName: "Chai", Category: "Beverages", Supplier: "Exotic Liquids", UnitPrice: 18.00 },
            { ProductName: "Chang", Category: "Beverages", Supplier: "Exotic Liquids", UnitPrice: 19.00 },
            { ProductName: "Aniseed Syrup", Category: "Condiments", Supplier: "Exotic Liquids", UnitPrice: 10.00 },
            { ProductName: "Chef Anton's Gumbo Mix", Category: "Grains/Cereals", Supplier: "New Orleans Cajun Delights", UnitPrice: 21.35 },
            { ProductName: "Grandma's Boysenberry Spread", Category: "Condiments", Supplier: "Grandma Kelly's Homestead", UnitPrice: 25.00 },
            { ProductName: "Uncle Bob's Organic Dried Pears", Category: "Produce", Supplier: "Grandma Kelly's Homestead", UnitPrice: 30.00 },
            { ProductName: "Northwoods Cranberry Sauce", Category: "Condiments", Supplier: "Grandma Kelly's Homestead", UnitPrice: 40.00 },
            { ProductName: "Mishi Kobe Niku", Category: "Meat/Poultry", Supplier: "Tokyo Traders", UnitPrice: 97.00 },
            { ProductName: "Ikura", Category: "Seafood", Supplier: "Tokyo Traders", UnitPrice: 31.00 },
            { ProductName: "Queso Cabrales", Category: "Dairy Products", Supplier: "Cooperativa de Quesos 'Las Cabras'", UnitPrice: 21.00 }
        ];

        function generateReport() {
            const tbody = document.getElementById('inventory-body');
            const summaryBar = document.getElementById('summary-bar');
            const totalCount = document.getElementById('total-count');
            const dateDisplay = document.getElementById('report-date');

            // Set Date
            dateDisplay.innerText = `Report Date: ${new Date().toLocaleDateString()}`;

            // Calculation Logic: Average Unit Price
            const totalSum = inventoryData.reduce((acc, item) => acc + item.UnitPrice, 0);
            const averagePrice = totalSum / inventoryData.length;

            // Update Summary
            summaryBar.innerHTML = `Average Unit Price: $${averagePrice.toFixed(2)}`;
            totalCount.innerText = inventoryData.length;

            // Render Rows
            inventoryData.forEach(item => {
                const row = document.createElement('tr');
                
                // Conditional Formatting Logic
                const priceClass = item.UnitPrice > averagePrice ? 'above-average' : '';

                row.innerHTML = `
                    <td>${item.ProductName}</td>
                    <td>${item.Category}</td>
                    <td>${item.Supplier}</td>
                    <td class="text-right ${priceClass}">$${item.UnitPrice.toFixed(2)}</td>
                `;
                tbody.appendChild(row);
            });
        }

        // Initialize
        document.addEventListener('DOMContentLoaded', generateReport);
    </script>
</body>
</html>

Conclusion

Factify is raising millions to solve the problem of "dumb documents." At Code On Time, we are solving it by giving you a "smart employee."

You don't need to replace the PDF. You just need to stop hard-coding how it's made. By moving from the Report Viewer to the Digital Co-Worker, you aren't just generating reports; you are deploying an autonomous agent that understands your business data and can present it in any format the world requires.

The legacy code is still there—it still works—but the future is already here.

Labels: AI, HATEOAS, RESTful