[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EG3FMZH962XW16Q1FCCPZP":3,"skill-same:01M6EG3FMZH962XW16Q1FCCPZP":45},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":18,"linkOnly":19,"markdownContent":20,"markdownBody":20,"frontMatter":21,"refName":22,"commitSha":23,"skillDirectory":24,"forksCount":25,"repositoryHtmlUrl":26,"repositoryHomepageUrl":27,"ownerHtmlUrl":28,"ownerBlogUrl":6,"ownerDescription":29,"updatedAt":30,"install":31,"resources":44},"01M6EG3FMZH962XW16Q1FCCPZP","sap",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FTerminalSkills\u002Fskills\u002Fsap\u002FSKILL.md",[],[5],"2026-11-10 10:12:30",false,"# SAP\n\n## Overview\n\nIntegrate with SAP — the enterprise ERP backbone. This skill covers SAP S\u002F4HANA Cloud and on-premise APIs (OData V2\u002FV4, BAPI\u002FRFC), SAP Business One (Service Layer), SAP BTP application development, SAP CAP (Cloud Application Programming model), SAP Cloud SDK for JavaScript\u002FTypeScript, master data management, procurement and sales document automation, and integration patterns for connecting SAP with external systems.\n\n## Instructions\n\n### Step 1: Authentication & Connectivity\n\n**S\u002F4HANA Cloud — OAuth2 Client Credentials:**\n```bash\nexport SAP_TOKEN_URL=\"https:\u002F\u002Fyour-tenant.authentication.eu10.hana.ondemand.com\u002Foauth\u002Ftoken\"\nexport SAP_CLIENT_ID=\"client-id\"\nexport SAP_CLIENT_SECRET=\"client-secret\"\nexport SAP_BASE_URL=\"https:\u002F\u002Fmy-s4hana.com\u002Fsap\u002Fopu\u002Fodata\u002Fsap\"\n```\n\n```typescript\nasync function getSAPToken() {\n  const res = await fetch(process.env.SAP_TOKEN_URL!, {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application\u002Fx-www-form-urlencoded\",\n      Authorization: \"Basic \" + Buffer.from(`${SAP_CLIENT_ID}:${SAP_CLIENT_SECRET}`).toString(\"base64\"),\n    },\n    body: \"grant_type=client_credentials\",\n  });\n  return (await res.json()).access_token;\n}\n\nasync function sapApi(method: string, path: string, token: string, body?: any) {\n  const res = await fetch(`${process.env.SAP_BASE_URL}${path}`, {\n    method,\n    headers: {\n      Authorization: `Bearer ${token}`, \"Content-Type\": \"application\u002Fjson\", Accept: \"application\u002Fjson\",\n      ...(method !== \"GET\" ? { \"X-CSRF-Token\": await getCSRFToken(token) } : {}),\n    },\n    body: body ? JSON.stringify(body) : undefined,\n  });\n  if (!res.ok) throw new Error(`SAP ${res.status}: ${await res.text()}`);\n  return res.json();\n}\n```\n\n**SAP Business One — Service Layer:**\n```typescript\nconst B1_URL = \"https:\u002F\u002Fyour-b1-server:50000\u002Fb1s\u002Fv1\";\n\nasync function b1Login() {\n  const res = await fetch(`${B1_URL}\u002FLogin`, {\n    method: \"POST\", headers: { \"Content-Type\": \"application\u002Fjson\" },\n    body: JSON.stringify({ CompanyDB: \"SBODemoUS\", UserName: \"manager\", Password: \"password\" }),\n  });\n  return res.headers.get(\"set-cookie\"); \u002F\u002F Session cookie\n}\n\nasync function b1Api(method: string, path: string, session: string, body?: any) {\n  const res = await fetch(`${B1_URL}${path}`, {\n    method, headers: { \"Content-Type\": \"application\u002Fjson\", Cookie: session },\n    body: body ? JSON.stringify(body) : undefined,\n  });\n  return res.json();\n}\n```\n\n### Step 2: S\u002F4HANA OData APIs\n\n**Business Partners:**\n```typescript\nconst customers = await sapApi(\"GET\",\n  \"\u002FAPI_BUSINESS_PARTNER\u002FA_BusinessPartner?$filter=BusinessPartnerCategory eq '1'&$select=BusinessPartner,BusinessPartnerFullName,Industry&$top=50\", token);\n\nawait sapApi(\"POST\", \"\u002FAPI_BUSINESS_PARTNER\u002FA_BusinessPartner\", token, {\n  BusinessPartnerCategory: \"1\", BusinessPartnerFullName: \"TechStart GmbH\",\n  SearchTerm1: \"TECHSTART\", Industry: \"IT\",\n  to_BusinessPartnerAddress: [{ Country: \"DE\", CityName: \"Berlin\", StreetName: \"Friedrichstraße\", HouseNumber: \"123\", PostalCode: \"10117\" }],\n});\n```\n\n**Sales Orders:**\n```typescript\nawait sapApi(\"POST\", \"\u002FAPI_SALES_ORDER_SRV\u002FA_SalesOrder\", token, {\n  SalesOrderType: \"OR\", SalesOrganization: \"1010\", DistributionChannel: \"10\",\n  SoldToParty: \"CUSTOMER_BP_ID\", PurchaseOrderByCustomer: \"PO-2026-001\",\n  to_Item: [\n    { Material: \"TG11\", RequestedQuantity: \"10\", RequestedQuantityUnit: \"EA\", NetPriceAmount: \"100.00\", NetPriceCurrency: \"EUR\" },\n    { Material: \"TG12\", RequestedQuantity: \"5\", RequestedQuantityUnit: \"EA\", NetPriceAmount: \"250.00\", NetPriceCurrency: \"EUR\" },\n  ],\n});\n```\n\n**Purchase Orders & Material Master:**\n```typescript\nawait sapApi(\"POST\", \"\u002FAPI_PURCHASEORDER_PROCESS_SRV\u002FA_PurchaseOrder\", token, {\n  PurchaseOrderType: \"NB\", CompanyCode: \"1010\", Supplier: \"VENDOR_BP_ID\",\n  to_PurchaseOrderItem: [{ Material: \"RAW-001\", OrderQuantity: \"1000\", PurchaseOrderQuantityUnit: \"KG\",\n    NetPriceAmount: \"5.50\", DocumentCurrency: \"EUR\", Plant: \"1010\" }],\n});\n\nconst material = await sapApi(\"GET\", \"\u002FAPI_PRODUCT_SRV\u002FA_Product('TG11')?$expand=to_Description,to_Plant\", token);\n```\n\n### Step 3: SAP Business One Operations\n\n```typescript\n\u002F\u002F Sales order → invoice → payment flow\nconst order = await b1Api(\"POST\", \"\u002FOrders\", session, {\n  CardCode: \"C20000\", DocDate: \"2026-02-18\",\n  DocumentLines: [\n    { ItemCode: \"A00001\", Quantity: 10, UnitPrice: 100 },\n    { ItemCode: \"A00002\", Quantity: 5, UnitPrice: 250 },\n  ],\n});\n\nconst invoice = await b1Api(\"POST\", \"\u002FInvoices\", session, {\n  CardCode: \"C20000\",\n  DocumentLines: [\n    { BaseType: 17, BaseEntry: order.DocEntry, BaseLine: 0 },\n    { BaseType: 17, BaseEntry: order.DocEntry, BaseLine: 1 },\n  ],\n});\n\nawait b1Api(\"POST\", \"\u002FIncomingPayments\", session, {\n  CardCode: \"C20000\", DocDate: \"2026-02-18\", CashSum: 1750,\n  PaymentInvoices: [{ DocEntry: invoice.DocEntry, SumApplied: 1750, InvoiceType: \"it_Invoice\" }],\n});\n```\n\n### Step 4: SAP CAP (Cloud Application Programming)\n\n```bash\nnpm install -g @sap\u002Fcds-dk\ncds init my-project && cd my-project && npm install\n```\n\n**Data model** (`db\u002Fschema.cds`):\n```cds\nnamespace my.project;\nentity Products { key ID: UUID; name: String(100); price: Decimal(10,2); stock: Integer; category: Association to Categories; }\nentity Categories { key ID: UUID; name: String(50); products: Association to many Products on products.category = $self; }\nentity Orders { key ID: UUID; orderDate: Date; customer: String(100); status: String(20) default 'New';\n  items: Composition of many OrderItems on items.order = $self; totalAmount: Decimal(12,2); }\nentity OrderItems { key ID: UUID; order: Association to Orders; product: Association to Products; quantity: Integer; unitPrice: Decimal(10,2); }\n```\n\n**Service logic** (`srv\u002Fcatalog-service.js`):\n```javascript\nconst cds = require(\"@sap\u002Fcds\");\nmodule.exports = class CatalogService extends cds.ApplicationService {\n  init() {\n    const { Products, Orders } = this.entities;\n    this.before(\"CREATE\", Orders, async (req) => {\n      for (const item of req.data.items) {\n        const product = await SELECT.one.from(Products).where({ ID: item.product_ID });\n        if (!product) throw req.reject(404, `Product ${item.product_ID} not found`);\n        if (product.stock \u003C item.quantity) throw req.reject(409, `Insufficient stock for ${product.name}`);\n        item.unitPrice = product.price;\n      }\n      req.data.totalAmount = req.data.items.reduce((s, i) => s + i.unitPrice * i.quantity, 0);\n    });\n    this.after(\"CREATE\", Orders, async (order) => {\n      for (const item of order.items)\n        await UPDATE(Products).set({ stock: { \"-=\": item.quantity } }).where({ ID: item.product_ID });\n    });\n    return super.init();\n  }\n};\n```\n\nRun locally: `cds watch` → API at `http:\u002F\u002Flocalhost:4004\u002Fcatalog`.\n\n### Step 5: Integration Patterns\n\n**SAP → External (polling):**\n```typescript\nimport cron from \"node-cron\";\nlet lastCheck = new Date().toISOString();\ncron.schedule(\"*\u002F5 * * * *\", async () => {\n  const token = await getSAPToken();\n  const orders = await sapApi(\"GET\",\n    `\u002FAPI_SALES_ORDER_SRV\u002FA_SalesOrder?$filter=CreationDate gt datetime'${lastCheck}'&$expand=to_Item`, token);\n  for (const order of orders.d.results) {\n    await externalApi.createOrder({ sapOrderId: order.SalesOrder, customer: order.SoldToParty,\n      items: order.to_Item.results.map((i: any) => ({ material: i.Material, quantity: parseFloat(i.OrderQuantity) })),\n    });\n  }\n  lastCheck = new Date().toISOString();\n});\n```\n\n**External → SAP (Shopify webhook):**\n```typescript\napp.post(\"\u002Fwebhook\u002Fshopify\u002Forder\", async (req, res) => {\n  const token = await getSAPToken();\n  const items = req.body.line_items.map((item: any) => ({\n    Material: item.sku, RequestedQuantity: String(item.quantity),\n    NetPriceAmount: String(item.price), NetPriceCurrency: req.body.currency,\n  }));\n  await sapApi(\"POST\", \"\u002FAPI_SALES_ORDER_SRV\u002FA_SalesOrder\", token, {\n    SalesOrderType: \"OR\", SalesOrganization: \"1010\", DistributionChannel: \"10\",\n    SoldToParty: \"CUSTOMER_BP_ID\", to_Item: items,\n  });\n  res.sendStatus(200);\n});\n```\n\n## Examples\n\n### Example 1: Automated procurement workflow with inventory checks\n\n**User prompt:** \"When our warehouse stock for material RAW-001 drops below 500 KG, automatically create a purchase order to our primary vendor for 2000 KG. Check stock daily and log every PO created.\"\n\nThe agent will set up a cron job that runs daily, querying the SAP Product API for material RAW-001's current stock level. If stock is below 500, it will create a purchase order via the Purchase Order API with the vendor BP ID, 2000 KG quantity, the standard plant and storage location, and log the PO number and creation date. It will also check for any existing open POs for the same material to avoid duplicate orders.\n\n### Example 2: E-commerce to SAP sales order sync\n\n**User prompt:** \"Build a webhook endpoint that receives Shopify orders and creates matching sales orders in S\u002F4HANA with all line items mapped by SKU to SAP material numbers, including a customer lookup by email.\"\n\nThe agent will create an Express POST endpoint that receives Shopify order webhooks, extracts customer email and line items, queries the Business Partner API to find an existing customer by email (or creates a new one), maps each Shopify line item's SKU to an SAP material number, and creates a sales order via the Sales Order API with all items, quantities, prices, and the customer reference. It will return a 200 status and log the SAP sales order number for reconciliation.\n\n## Guidelines\n\n- **Always fetch a CSRF token before write operations** — S\u002F4HANA on-premise requires an `X-CSRF-Token: Fetch` GET request before any POST\u002FPATCH\u002FDELETE; cache the token and refresh on 403 responses.\n- **Use `$select` and `$top` on all OData queries** — SAP OData endpoints return all fields by default, which is slow for entities with hundreds of columns; always limit fields and page size.\n- **Handle SAP date formats carefully** — OData V2 uses `\u002FDate(timestamp)\u002F` format while V4 uses ISO 8601; parse dates explicitly rather than relying on automatic conversion.\n- **Use SAP Cloud SDK for type safety** — the generated VDM (Virtual Data Model) clients provide compile-time checking of field names, filters, and navigation properties.\n- **Rate-limit polling integrations** — SAP systems have finite OData throughput; poll no more frequently than every 5 minutes and use `$filter` on timestamps to minimize payload size.\n- **Test against the SAP API Business Hub sandbox** — use `api.sap.com` sandbox endpoints during development to avoid impacting production or QA systems.\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002FTerminalSkills\u002Fskills\u002Fsap",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":32,"prompt":42,"zipAvailable":43},[33,36,39],{"tool":34,"command":35},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill sap",{"tool":37,"command":38},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill sap",{"tool":40,"command":41},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill sap","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FTerminalSkills\u002Fskills\u002Fsap\u002FSKILL.md",true,[],[46,60,70,79,85,92],{"skillNo":47,"name":48,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":49,"categories":50,"keywords":56,"publishedAt":18},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[51],{"categoryNo":52,"categoryName":53,"taxonomyType":54,"taxonomyTypeDesc":55},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[57,58,59],"model","model hierarchy","model hierarchy skill",{"skillNo":61,"name":62,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":66,"publishedAt":18},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[65],{"categoryNo":52,"categoryName":53,"taxonomyType":54,"taxonomyTypeDesc":55},[67,68,69],"MCP","zoom","zoom mcp",{"skillNo":71,"name":72,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":18},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[76,77,78,68],"sdk","zoom apps sdk","apps",{"skillNo":80,"name":81,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":18},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[81],{"skillNo":86,"name":87,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":91,"publishedAt":18},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[90],{"categoryNo":52,"categoryName":53,"taxonomyType":54,"taxonomyTypeDesc":55},[87],{"skillNo":93,"name":94,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":18},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[94]]