C-level sponsors want slick Fiori apps, but SAP backlogs say “six-month rollout.” In this guide we show how a Micro-GCC squad shipped six Fiori Elements apps in 20 working days by:
Copy-paste Yeoman commands, CAP annotations, and Azure Bot JSON included.
Waterfall Fiori plan: design workshop ➝ SAP Build wire-frames ➝ hand-coded UI5 ➝ QA ➝ cut-over.
Pain points:
Our fix: Generate 80 % of UI from CDS, keep logic in CAP, deliver micro-approvals through Adaptive Cards—two-sprint cadence.
| Layer | Tool | Why |
| UI generator | Fiori Elements + Fiori Tools Yeoman | No hand-coded XML for CRUD screens |
| Micro-tasks | Adaptive Cards + Teams Bot | 90-second approvals without launching SAP |
| OData service | CAP Node.js (srv) | Shares annotations & logic |
| State | HANA Cloud | Single source of truth |
| CI/CD | GitHub Actions + Cloud Transport Management (CTMS) | Zero manual transports |
| Cache busting | sap-ui-cachebuster + CDN headers | Users always get latest JS/CSS |
Total dev image size: 850 MB; pipeline duration: ≈ 6 min.
bash
CopyEdit
npm install -g @sap/generator-fiori
yo @sap/fiori-freestyle myorders \
–platform cp \
–odata https://<cap>/v4/sales \
–entity SalesOrder \
–list yes \
–detail yes
Yeoman creates:
markdown
CopyEdit
myorders/
webapp/
pages/
manifest.json
ui5.yaml
db/schema.cds
cds
CopyEdit
using { cuid } from ‘@sap/cds/common’;
entity SalesOrder : cuid {
key SalesOrderID : String(10);
@title : ‘Net Value’
NetValue : Decimal(15,2);
@title : ‘Currency’
Currency : String(5);
}
srv/annotations.cds
cds
CopyEdit
using my.sales as sales from ‘../db/schema’;
annotate sales.SalesOrder with {
@UI : {
LineItem : [ {Value : sales.SalesOrderID}, {Value : sales.NetValue} ],
Identifiable : { Title : {Value : sales.SalesOrderID} }
}
}
Re-run cds deploy → metadata changes auto-appear in UI after cache-buster.
ui5.yaml
yaml
CopyEdit
builder:
customTasks:
– name: ui5-task-cachebuster
github/workflows/ui.yml
yaml
CopyEdit
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v4
– name: Install tooling
run: npm ci
– name: Build
run: ui5 build –all
– name: Deploy
run: cf deploy myorders.zip –dest dev-space
Pipeline time: 3 m 40 s.
By Sprint 1 Demo Friday, business users saw live list-report and object pages for Sales Orders, Customers, and Pricing Conditions.
cards/approve.json
json
CopyEdit
{
“$schema”: “http://adaptivecards.io/schemas/adaptive-card.json”,
“type”:”AdaptiveCard”,
“version”:”1.5″,
“body”:[
{“type”:”TextBlock”,”text”:”Sales Order ${SalesOrderID}”,”weight”:”Bolder”},
{“type”:”TextBlock”,”text”:”Net Value: ${NetValue} ${Currency}”},
{“type”:”Input.Text”,”id”:”comment”,”placeholder”:”Optional comment”}
],
“actions”:[
{“type”:”Action.Submit”,”title”:”Approve”,”data”:{“action”:”approve”}},
{“type”:”Action.Submit”,”title”:”Reject”,”style”:”destructive”,”data”:{“action”:”reject”}}
]
}
bot/app.js
js
CopyEdit
const { TeamsActivityHandler, CardFactory } = require(‘@microsoft/teams-ai’);
bot.onMessage(async (ctx) => {
const so = await SELECT.one.from(SalesOrder).where({SalesOrderID: ctx.message.text});
const card = CardFactory.adaptiveCard(require(‘./cards/approve.json’), so);
await ctx.sendActivity({ attachments: [ card ] });
});
bot.onInvokeActivity(async (ctx) => {
const { action } = ctx.activity.value;
if (action === ‘approve’) await patchStatus(ctx, ‘APPROVED’);
else await patchStatus(ctx, ‘REJECTED’);
return { status: 200 };
});
bot/workflows/ci.yml builds Docker image, pushes to Azure Container Apps, updates Teams manifest via Graph API.
| KPI | Before (Email Workflow) | After (Adaptive Card) |
| Median approval time | 6 h 20 m | 21 m |
| Missed SLA (>24 h) | 13 % | 0.5 % |
| Mobile engagement | N/A | 72 % of actions |
| Day | Activity |
| Sprint 1 Day 1 | Yeoman scaffolds 3 apps; GitHub pipeline ready. |
| Day 3 | CDS annotations complete; first demos. |
| Day 5 | AT tests pass; deploy to QA. |
| Sprint 2 Day 1 | Teams Bot scaffold; Adaptive card template. |
| Day 3 | Webhook to CAP service for status updates. |
| Day 4 | Security & persona tests. |
| Day 5 | 20 % pilot group; cut-over Monday next sprint. |
Total dev hours: ~64; feedback loop < 24 h each sprint.
| Pitfall | Fix |
| Cache-busting ignored | Add cachebuster-info.json & CDN header cache-control: max-age=0 |
| CDS enum not rendered in list | Use @Common.ValueList annotation referencing DDIC domain |
| Adaptive card timeout | Increase Bot Azure Timeout to 10 s or pre-warm function |
| CAP OData $batch not enabled | Set odata.version: 4 and odata.batch: true in package.json |
| Transport collisions (Neo) | Use Cloud Transport Management; lock older transport serial |
| Metric | Legacy Workflow | Two-Sprint Factory |
| Dev effort / CRUD app | 120 h | 18 h |
| Median user click → approve | 6 h | 21 m |
| UI regression bugs / rel. | 9 | 2 |
| Infra cost (BTP HTML5 repo) | €0.04/hr | same |
ROI: 6× faster dev, 88 % faster approvals, happier auditors (mobile audit trail).