{"id":5924,"date":"2025-01-11T14:18:22","date_gmt":"2025-01-11T13:18:22","guid":{"rendered":"https:\/\/gpmfactory.com\/?p=5924"},"modified":"2025-01-11T18:31:36","modified_gmt":"2025-01-11T17:31:36","slug":"oracle-document-generator-pre-built-function","status":"publish","type":"post","link":"https:\/\/gpmfactory.com\/index.php\/2025\/01\/11\/oracle-document-generator-pre-built-function\/","title":{"rendered":"Oracle Document Generator Pre-Built function"},"content":{"rendered":"\n\n\n<p>Il s&rsquo;agit d&rsquo;un service pr\u00eat \u00e0 l&#8217;emploi sous forme d&rsquo;image Docker, disponible dans le cloud Oracle, qui permet de g\u00e9n\u00e9rer des documents au format PDF \u00e0 partir de :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Une source de donn\u00e9es au format JSON<\/li>\n\n\n\n<li>Un mod\u00e8le (template) au format MS Word ou MS Excel<\/li>\n<\/ul>\n\n\n\n<p>Ce service s&rsquo;appuie sur l&rsquo;utilisation du service de stockage (Storage) car les templates et les documents g\u00e9n\u00e9r\u00e9s doivent \u00eatre enregistr\u00e9s dans des <em>buckets<\/em>. Les donn\u00e9es, elles, peuvent \u00eatre soit envoy\u00e9es \u00e0 la vol\u00e9e, soit \u00eatre enregistr\u00e9es au format JSON dans un <em>bucket <\/em>\u00e9galement.<\/p>\n\n\n\n<p>Dans mon test, j&rsquo;ai pu v\u00e9rifier que l&rsquo;on pouvait produire des documents avec plusieurs niveaux de r\u00e9p\u00e9titivit\u00e9.<br>L&rsquo;avantage principal de ce service <em>Document Generator<\/em> est qu&rsquo;on b\u00e9n\u00e9ficie de toute la puissance de MS Word pour la conception.<\/p>\n\n\n\n<p>La tarification est plut\u00f4t cool si on reste en dessous d&rsquo;un seuil de 400.000 appels\/mois.<br>cf <a href=\"https:\/\/www.oracle.com\/cloud\/price-list\/\" target=\"_blank\" rel=\"noreferrer noopener\">Price List<\/a> (chercher le mot clef: <em>function<\/em>)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Echantillon<\/h3>\n\n\n\n<p>J&rsquo;ai repris le <a href=\"https:\/\/create.microsoft.com\/fr-fr\/templates\/factures\" target=\"_blank\" rel=\"noreferrer noopener\">mod\u00e8le de facture<\/a> (<em>Facture de vente professionnelle<\/em>) qui est fourni avec MS Word.<\/p>\n\n\n\n<p>J&rsquo;ai enlev\u00e9 tous les <a href=\"https:\/\/support.microsoft.com\/en-us\/office\/about-content-controls-283b1e29-0b77-4781-b236-2d02c1cce1c2\" target=\"_blank\" rel=\"noreferrer noopener\">content controls<\/a> et je les ai remplac\u00e9s par des libell\u00e9s statiques et par des tags correspondant \u00e0 la source de donn\u00e9es.<\/p>\n\n\n\n<p>Les structures r\u00e9p\u00e9titives font l&rsquo;objet de tag particuliers: {#node} {\/node}<br>cf <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/Functions\/non-dita\/DocGenPBF-doc\/markdown\/DocGen-Template-Tags.htm\" target=\"_blank\" rel=\"noreferrer noopener\">documentation sur les tags pour MS Word<\/a><br>Dans cet exemple, il y a deux boucles imbriqu\u00e9es de contr\u00f4le (pour chaque facture, et pour chaque ligne de facture)<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><br>Appel de la fonction de g\u00e9n\u00e9ration de document<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Soit par l&rsquo;interface OCI CLI<\/li>\n\n\n\n<li>Soit en utilisant un SDK (l&rsquo;appli donn\u00e9e en exemple avec APEX utilise le SDK PL\/SQL)<\/li>\n\n\n\n<li>Soit en utilisant un appel REST assorti d&rsquo;un Web Credential d\u00e9fini dans APEX. <br>C&rsquo;est la m\u00e9thode que j&rsquo;ai utilis\u00e9e dans le cas pr\u00e9sent. Facile \u00e0 mettre en place dans APEX car le passage des credentials (qui n\u00e9cessite une signature de l&rsquo;appel) est masqu\u00e9 par APEX.<\/li>\n<\/ul>\n\n\n\n<p>cf <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/Functions\/Tasks\/functionsinvokingfunctions.htm\" target=\"_blank\" rel=\"noreferrer noopener\">Documentation sur l&rsquo;appel des PBF<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">R\u00e9sultat PDF<\/h4>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"863\" height=\"1024\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-863x1024.png\" alt=\"\" class=\"wp-image-5926\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-863x1024.png 863w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-253x300.png 253w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-768x911.png 768w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image.png 1104w\" sizes=\"auto, (max-width: 863px) 100vw, 863px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Components<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Payload<\/h4>\n\n\n\n<p>Ce sont les informations envoy\u00e9es \u00e0 la fonction \u00ab\u00a0Document Generator\u00a0\u00bb lui indiquant la localisation du template et de la source de donn\u00e9es. La <em>requestType<\/em> est SINGLE (un seul document en sortie) ou bien BATCH (autant de documents PDF qu&rsquo;il y a d&rsquo;items dans la source de donn\u00e9es qui, dans ce cas, doit se pr\u00e9senter comme un <em>array<\/em>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"requestType\": \"SINGLE\",\n  \"tagSyntax\": \"DOCGEN_1_0\",\n  \"data\": {\n    \"source\": \"OBJECT_STORAGE\",\n    \"namespace\": \"xxxxxxxxxxxxx\",\n    \"bucketName\": \"pdf\",\n    \"objectName\": \"orders.json\",\n    \"contentType\": \"application\/json\"\n  },\n  \"template\": {\n    \"source\": \"OBJECT_STORAGE\",\n    \"namespace\": \"xxxxxxxxxxxxx\",\n    \"bucketName\": \"pdf\",\n    \"objectName\": \"orders.docx\",\n    \"contentType\": \"application\/vnd.openxmlformats-officedocument.wordprocessingml.document\"\n  },\n  \"output\": {\n    \"target\": \"OBJECT_STORAGE\",\n    \"namespace\": \"xxxxxxxxxxxxx\",\n    \"bucketName\": \"pdf\",\n    \"objectName\": \"orders.pdf\",\n    \"contentType\": \"application\/pdf\"\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">MS Word template (orders.docx)<\/h4>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"615\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-1-1024x615.png\" alt=\"\" class=\"wp-image-5927\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-1-1024x615.png 1024w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-1-300x180.png 300w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-1-768x462.png 768w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-1-1536x923.png 1536w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2025\/01\/image-1-2048x1231.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Data source orders.json<\/h4>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code>{\n\t\"ORDERS\": {\n\t\t\"HEADER\": {\n\t\t\t\"report_date\": \"Friday 10 January 2025\",\n\t\t\t\"TITLE\": \"\",\n\t\t\t\"DD\": \"Friday 10\",\n\t\t\t\"D\": \"10\",\n\t\t\t\"DAY\": \"Friday\",\n\t\t\t\"MONTH\": \"January\",\n\t\t\t\"YEAR\": \"2025\"\n\t\t},\n\t\t\"ORDERS_REC\": &#091;\n\t\t\t{\n\t\t\t\t\"ORDER_ID\": \"652\",\n\t\t\t\t\"ORDER_DATETIME\": \"2023-08-25T03:30:13.000000\",\n\t\t\t\t\"CUSTOMER_ID\": \"374\",\n\t\t\t\t\"ORDER_STATUS\": \"COMPLETE\",\n\t\t\t\t\"STORE_ID\": \"1\",\n\t\t\t\t\"CUSTOMERS_CUSTOMER_ID\": \"374\",\n\t\t\t\t\"CUSTOMERS_FULL_NAME\": \"Irene Moore\",\n\t\t\t\t\"CUSTOMERS_EMAIL_ADDRESS\": \"irene.moore@internalmail\",\n\t\t\t\t\"STORES_STORE_ID\": \"1\",\n\t\t\t\t\"STORES_STORE_NAME\": \"Online\",\n\t\t\t\t\"STORES_WEB_ADDRESS\": \"https:\/\/www.example.com\",\n\t\t\t\t\"ORDER_FOOTER_FOP_V\": {\n\t\t\t\t\t\"ORDER_FOOTER_FOP_V_ORDERS_REC\": {\n\t\t\t\t\t\t\"ORDER_ID\": \"652\",\n\t\t\t\t\t\t\"TOTAL\": \"143.24\",\n\t\t\t\t\t\t\"TVA\": \"28.65\",\n\t\t\t\t\t\t\"TTC\": \"171.89\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"ORDER_ITEMS_FOP_V\": {\n\t\t\t\t\t\"ORDER_ITEMS_FOP_V_ORDERS_REC\": &#091;\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"ORDER_ID\": \"652\",\n\t\t\t\t\t\t\"PRODUCT_ID\": \"13\",\n\t\t\t\t\t\t\"PRODUCT_NAME\": \"Boy's Hoodie (Grey)\",\n\t\t\t\t\t\t\"QUANTITY\": \"2\",\n\t\t\t\t\t\t\"UNIT_PRICE\": \"12.64\",\n\t\t\t\t\t\t\"MNT\": \"25.28\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"ORDER_ID\": \"652\",\n\t\t\t\t\t\t\"PRODUCT_ID\": \"44\",\n\t\t\t\t\t\t\"PRODUCT_NAME\": \"Women's Coat (Black)\",\n\t\t\t\t\t\t\"QUANTITY\": \"3\",\n\t\t\t\t\t\t\"UNIT_PRICE\": \"39.32\",\n\t\t\t\t\t\t\"MNT\": \"117.96\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"ORDER_ID\": \"653\",\n\t\t\t\t\"ORDER_DATETIME\": \"2023-08-25T07:17:39.000000\",\n\t\t\t\t\"CUSTOMER_ID\": \"293\",\n\t\t\t\t\"ORDER_STATUS\": \"COMPLETE\",\n\t\t\t\t\"STORE_ID\": \"1\",\n\t\t\t\t\"CUSTOMERS_CUSTOMER_ID\": \"293\",\n\t\t\t\t\"CUSTOMERS_FULL_NAME\": \"Diana Fowler\",\n\t\t\t\t\"CUSTOMERS_EMAIL_ADDRESS\": \"diana.fowler@internalmail\",\n\t\t\t\t\"STORES_STORE_ID\": \"1\",\n\t\t\t\t\"STORES_STORE_NAME\": \"Online\",\n\t\t\t\t\"STORES_WEB_ADDRESS\": \"https:\/\/www.example.com\",\n\t\t\t\t\"ORDER_FOOTER_FOP_V\": {\n\t\t\t\t\t\"ORDER_FOOTER_FOP_V_ORDERS_REC\": {\n\t\t\t\t\t\t\"ORDER_ID\": \"653\",\n\t\t\t\t\t\t\"TOTAL\": \"230.48\",\n\t\t\t\t\t\t\"TVA\": \"46.1\",\n\t\t\t\t\t\t\"TTC\": \"276.58\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"ORDER_ITEMS_FOP_V\": {\n\t\t\t\t\t\"ORDER_ITEMS_FOP_V_ORDERS_REC\": &#091;\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"ORDER_ID\": \"653\",\n\t\t\t\t\t\t\"PRODUCT_ID\": \"28\",\n\t\t\t\t\t\t\"PRODUCT_NAME\": \"Men's Hoodie (Red)\",\n\t\t\t\t\t\t\"QUANTITY\": \"3\",\n\t\t\t\t\t\t\"UNIT_PRICE\": \"10.24\",\n\t\t\t\t\t\t\"MNT\": \"30.72\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"ORDER_ID\": \"653\",\n\t\t\t\t\t\t\"PRODUCT_ID\": \"36\",\n\t\t\t\t\t\t\"PRODUCT_NAME\": \"Women's Trousers (Blue)\",\n\t\t\t\t\t\t\"QUANTITY\": \"3\",\n\t\t\t\t\t\t\"UNIT_PRICE\": \"49.12\",\n\t\t\t\t\t\t\"MNT\": \"147.36\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"ORDER_ID\": \"653\",\n\t\t\t\t\t\t\"PRODUCT_ID\": \"12\",\n\t\t\t\t\t\t\"PRODUCT_NAME\": \"Boy's Socks (White)\",\n\t\t\t\t\t\t\"QUANTITY\": \"5\",\n\t\t\t\t\t\t\"UNIT_PRICE\": \"10.48\",\n\t\t\t\t\t\t\"MNT\": \"52.4\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t]\n\t}\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Remarques &amp; limitations <\/h3>\n\n\n\n<p>Je n&rsquo;ai pas r\u00e9ussi \u00e0 d\u00e9clencher un saut de page avec {?pagebreak} tag.<\/p>\n\n\n\n<p>Si les noms de tags sont volumineux, cela diminue la lisibilit\u00e9 du template. Il aurait fallu pouvoir cacher les informatiques techniques, un peu comme dans un <em>Content Control<\/em> de Word.<\/p>\n\n\n\n<p>Je n&rsquo;ai pas vu de tag conditionnant un paragraphe (bien que le tag <em>filter <\/em>y ressemble un peu).<\/p>\n\n\n\n<p>Le premier appel \u00e0 la fonction (<em>cold start<\/em>) est tr\u00e8s long (une minute). Les suivants sont rapides (2 sec).<br>La documentation donne des <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/Functions\/Tasks\/functionsusingprovisionedconcurrency.htm\" target=\"_blank\" rel=\"noreferrer noopener\">explications l\u00e0-dessus<\/a> (<em>provisioned concurrency<\/em>). Il est ainsi possible de maintenir un bon niveau de r\u00e9activit\u00e9 en param\u00e9trant la <em>provisioned concurrency<\/em>.<br>Garder cependant en t\u00eate que le fait de maintenir des fonctions \u00ab\u00a0pr\u00eate \u00e0 bondir\u00a0\u00bb, mais non sollicit\u00e9es, est factur\u00e9 en sus. (cf pricing des fonctions) <\/p>\n\n\n\n<p>Je pense que ce service est destin\u00e9 \u00e0 la production de documents unitaires (un appel, un document). J&rsquo;ai parfois constat\u00e9, sinon, des pb de timeout. D&rsquo;apr\u00e8s la doc, il semble qu&rsquo;une fonction ne puisse produire plus de <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/Functions\/Concepts\/functionsoverview.htm\" target=\"_blank\" rel=\"noreferrer noopener\">6 Mo en output<\/a>. Cela expliquerait le timeout car il y avait une centaine de factures \u00e0 g\u00e9n\u00e9rer et chacune prend environ 60 Ko dans le pdf.<\/p>\n\n\n\n<p>Dans le <em>payload<\/em>, il ne semble pas possible d&rsquo;indiquer une URL pour acc\u00e9der aux donn\u00e9es. C&rsquo;est soit INLINE, soit OBJECT_STORAGE.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Liens utiles<\/h3>\n\n\n\n<p><a href=\"https:\/\/blogs.oracle.com\/apex\/post\/seamless-pdf-generation-with-oracle-apex-and-oci-document-generator\">https:\/\/blogs.oracle.com\/apex\/post\/seamless-pdf-generation-with-oracle-apex-and-oci-document-generator<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/www.linkedin.com\/pulse\/pdf-generation-oci-pre-built-function-kris-rice-gbree\">https:\/\/www.linkedin.com\/pulse\/pdf-generation-oci-pre-built-function-kris-rice-gbree<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/tm-apex.hashnode.dev\/start-generating-dynamic-pdfs-in-apex-easily-with-oci-pre-built-functions\">https:\/\/tm-apex.hashnode.dev\/start-generating-dynamic-pdfs-in-apex-easily-with-oci-pre-built-functions<\/a><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Il s&rsquo;agit d&rsquo;un service pr\u00eat \u00e0 l&#8217;emploi sous forme d&rsquo;image Docker, disponible dans le cloud Oracle, qui permet de g\u00e9n\u00e9rer des documents au format&#8230;<\/p>\n","protected":false},"author":1,"featured_media":5948,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"ppma_author":[150],"class_list":["post-5924","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-non-classe"],"authors":[{"term_id":150,"user_id":1,"is_guest":0,"slug":"admin8700","display_name":"Patrick","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/209d5ed69b74d288390621ab4c1d3773?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/5924","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/comments?post=5924"}],"version-history":[{"count":23,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/5924\/revisions"}],"predecessor-version":[{"id":5959,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/5924\/revisions\/5959"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/media\/5948"}],"wp:attachment":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/media?parent=5924"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/categories?post=5924"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/tags?post=5924"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/ppma_author?post=5924"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}