{
  "_class" : "hudson.model.FreeStyleBuild",
  "actions" : [
    {
      "_class" : "hudson.model.CauseAction",
      "causes" : [
        {
          "_class" : "hudson.triggers.TimerTrigger$TimerTriggerCause",
          "shortDescription" : "Started by timer"
        }
      ]
    },
    {
      
    },
    {
      "_class" : "hudson.plugins.git.util.BuildData",
      "buildsByBranchName" : {
        "refs/remotes/origin/dev" : {
          "_class" : "hudson.plugins.git.util.Build",
          "buildNumber" : 735,
          "buildResult" : null,
          "marked" : {
            "SHA1" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
            "branch" : [
              {
                "SHA1" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
                "name" : "refs/remotes/origin/dev"
              }
            ]
          },
          "revision" : {
            "SHA1" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
            "branch" : [
              {
                "SHA1" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
                "name" : "refs/remotes/origin/dev"
              }
            ]
          }
        }
      },
      "lastBuiltRevision" : {
        "SHA1" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
        "branch" : [
          {
            "SHA1" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
            "name" : "refs/remotes/origin/dev"
          }
        ]
      },
      "remoteUrls" : [
        "https://github.com/younex1/erp-nuxt-frontend.git"
      ],
      "scmName" : ""
    },
    {
      
    },
    {
      
    },
    {
      "_class" : "org.jenkinsci.plugins.displayurlapi.actions.RunDisplayAction"
    }
  ],
  "artifacts" : [
    
  ],
  "building" : false,
  "description" : null,
  "displayName" : "#735",
  "duration" : 68175,
  "estimatedDuration" : 71602,
  "executor" : null,
  "fullDisplayName" : "build-logship-frontend #735",
  "id" : "735",
  "inProgress" : false,
  "keepLog" : false,
  "number" : 735,
  "queueId" : 5689,
  "result" : "SUCCESS",
  "timestamp" : 1789257600272,
  "url" : "https://jenkins.younex.de/view/all/job/build-logship-frontend/735/",
  "builtOn" : "",
  "changeSet" : {
    "_class" : "hudson.plugins.git.GitChangeSetList",
    "items" : [
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/pages/generate-shipments.vue",
          "server/api/orders/openso-generation-queue.get.ts",
          "app/composables/useShipperLogo.ts",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "f263d5555ef9086194f5d605f8dd33374754d72b",
        "timestamp" : 1789232580000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Generate shipments: shipper filter (queue-driven, same semantics as the qty-1 option) — openso-generation-queue accepts shipperIds + returns shipperId/shipperName; new 'Only selected shippers' option card with a logo multi-select (useShipperLogo composable centralises the carrier→logo mapping, fallback badge for unknown carriers), select all/clear + counter, submit gated until a shipper is picked, shipper shown in progress + result labels; options block restyled into 'Order filters' / 'Process options' groups with subtitle + ⓘ hover tooltip per option, previously hardcoded English options translated (en/de/es/ne)\u000a",
        "date" : "2026-09-12 19:03:00 +0200",
        "id" : "f263d5555ef9086194f5d605f8dd33374754d72b",
        "msg" : "Generate shipments: shipper filter (queue-driven, same semantics as the qty-1 option) — openso-generation-queue accepts shipperIds + returns shipperId/shipperName; new 'Only selected shippers' option card with a logo multi-select (useShipperLogo composable centralises the carrier→logo mapping, fallback badge for unknown carriers), select all/clear + counter, submit gated until a shipper is picked, shipper shown in progress + result labels; options block restyled into 'Order filters' / 'Process options' groups with subtitle + ⓘ hover tooltip per option, previously hardcoded English options translated (en/de/es/ne)",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/pages/generate-shipments.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/api/orders/openso-generation-queue.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "add",
            "file" : "app/composables/useShipperLogo.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/pages/users/users/index.vue",
          "server/api/admin/users/update.put.ts",
          "app/components/partners/PartnerDetailRecordUser.vue",
          "app/pages/users/users/create.vue",
          "server/api/telephony/me.get.ts",
          "app/components/DialButton.vue",
          "app/pages/users/external-users/create.vue",
          "server/utils/telephony/extensions.ts",
          "app/pages/users/external-users/[id]/duplicate.vue",
          "server/utils/telephony/dialAccess.ts",
          "app/composables/useClickToDial.ts",
          ".env-dev",
          "nuxt.config.ts",
          "server/api/telephony/extensions.get.ts",
          "server/api/telephony/dial.post.ts",
          "app/pages/users/leads/[id]/edit.vue",
          "server/utils/telephony/phoneActivity.ts",
          "app/pages/users/external-users/[id]/edit.vue",
          "app/assets/langs/shared.js",
          "app/components/sales/OrderForm.vue",
          "app/pages/users/users/[id]/edit.vue",
          "server/api/admin/users/store.post.ts",
          "app/components/partners/PartnerDetailRecordLocation.vue",
          "server/utils/telephony/amiClient.ts",
          "app/components/admin/UserForm.vue",
          ".env",
          "app/components/AgGrid/AgDialPhone.vue",
          "app/pages/users/users/[id]/view.vue",
          "app/pages/users/leads/index.vue",
          "app/components/SearchModal.vue",
          "app/components/admin/LeadForm.vue",
          "app/pages/users/external-users/index.vue",
          "app/pages/users/users/[id]/duplicate.vue",
          "app/pages/users/external-users/[id]/view.vue",
          "app/components/integrations/CommissionModal.vue",
          "server/utils/telephony/callTracker.ts",
          ".env-prod",
          "app/pages/users/leads/[id]/view.vue"
        ],
        "commitId" : "bd0aee220aeaa8e0f4543e7a844a66569e7a0f32",
        "timestamp" : 1789236101000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Click-to-dial via FreePBX/Asterisk AMI + AD_User.SIP_Extension CRUD — new <DialButton> next to every phone field (user/lead/contact forms + views, partner contacts/locations, order ship-to phone, commission modal, search results, phone columns of the user/lead lists via AgDialPhone), visible only for system users with a SIP_Extension (server-authoritative gate via JWT + models/ad_user, 10-min cache); click opens a popover with the PBX extension chooser (DeviceStateList with live state joined with ERP user names, own extension preselected, free entry, remembered per browser) and dials: POST /api/telephony/dial originates PJSIP/<ext> (Local/<ext>@from-internal for dialplan-routed extensions) with the target as caller-id, returns right after the originate is accepted (1.5 s fast-fail window for an unreachable phone); a detached per-call AMI tracker follows OriginateResponse/DialBegin/DialEnd/Hangup via the inherited __LOGSHIP_CALL_ID channel var and writes a C_ContactActivity (type PC, reached/not reached + duration, German cause texts) on the called AD_User (or the partner's contact user) with the service token — fail-soft, unref'd timers, 3 h cap, per-process; new runtimeConfig.asterisk + CLICK_TO_DIAL_ENABLED/ASTERISK_* env keys; user pages: admin-only SIP extension field (internal tab, system users only, greyed for non-admins), retrieve/save wiring, view row, 'SIP Ext.' grid column, update/store routes gate the key on isAdminVerified; FreePBX: [logship] manager user (manager_custom.conf, LAN ACL, originate/call/reporting) + channelvars=LOGSHIP_CALL_ID in manager.conf\u000a",
        "date" : "2026-09-12 20:01:41 +0200",
        "id" : "bd0aee220aeaa8e0f4543e7a844a66569e7a0f32",
        "msg" : "Click-to-dial via FreePBX/Asterisk AMI + AD_User.SIP_Extension CRUD — new <DialButton> next to every phone field (user/lead/contact forms + views, partner contacts/locations, order ship-to phone, commission modal, search results, phone columns of the user/lead lists via AgDialPhone), visible only for system users with a SIP_Extension (server-authoritative gate via JWT + models/ad_user, 10-min cache); click opens a popover with the PBX extension chooser (DeviceStateList with live state joined with ERP user names, own extension preselected, free entry, remembered per browser) and dials: POST /api/telephony/dial originates PJSIP/<ext> (Local/<ext>@from-internal for dialplan-routed extensions) with the target as caller-id, returns right after the originate is accepted (1.5 s fast-fail window for an unreachable phone); a detached per-call AMI tracker follows OriginateResponse/DialBegin/DialEnd/Hangup via the inherited __LOGSHIP_CALL_ID channel var and writes a C_ContactActivity (type PC, reached/not reached + duration, German cause texts) on the called AD_User (or the partner's contact user) with the service token — fail-soft, unref'd timers, 3 h cap, per-process; new runtimeConfig.asterisk + CLICK_TO_DIAL_ENABLED/ASTERISK_* env keys; user pages: admin-only SIP extension field (internal tab, system users only, greyed for non-admins), retrieve/save wiring, view row, 'SIP Ext.' grid column, update/store routes gate the key on isAdminVerified; FreePBX: [logship] manager user (manager_custom.conf, LAN ACL, originate/call/reporting) + channelvars=LOGSHIP_CALL_ID in manager.conf",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/pages/users/external-users/create.vue"
          },
          {
            "editType" : "add",
            "file" : "app/components/DialButton.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/api/admin/users/store.post.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/extensions.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/users/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/me.get.ts"
          },
          {
            "editType" : "add",
            "file" : "app/components/AgGrid/AgDialPhone.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/external-users/[id]/view.vue"
          },
          {
            "editType" : "edit",
            "file" : ".env-prod"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/leads/index.vue"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/phoneActivity.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/integrations/CommissionModal.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/users/[id]/view.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/users/create.vue"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/callTracker.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/SearchModal.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/admin/UserForm.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/sales/OrderForm.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/external-users/[id]/duplicate.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/users/[id]/edit.vue"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/dialAccess.ts"
          },
          {
            "editType" : "add",
            "file" : "app/composables/useClickToDial.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/users/[id]/duplicate.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/admin/LeadForm.vue"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/dial.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/external-users/[id]/edit.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/partners/PartnerDetailRecordLocation.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/partners/PartnerDetailRecordUser.vue"
          },
          {
            "editType" : "edit",
            "file" : ".env-dev"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/leads/[id]/view.vue"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/amiClient.ts"
          },
          {
            "editType" : "edit",
            "file" : "nuxt.config.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/api/admin/users/update.put.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/external-users/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/leads/[id]/edit.vue"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/extensions.get.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/utils/activityFormat.ts",
          "app/components/admin/LeadDetailRecordActivity.vue"
        ],
        "commitId" : "9a43b22bbf9db5090902c079f1d6bcf0c76fca38",
        "timestamp" : 1789236490000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Lead activities: rich rendering of activity notes — new app/utils/activityFormat.ts (escape → linkify → decorate; safe for v-html) renders line-initial / '·'-separated 'Label:' as bold, 'Label (…): https://…' lines as a labelled link chip (e.g. the online confirmation / signing link) instead of the raw token URL, URLs and e-mail addresses as links, file names (.pdf/.docx/…) as badges, **bold**/_italic_ markers, blank lines as paragraph gaps; notes written as simple HTML (<b>, <i>, <u>, <a>, lists, <br>) are sanitised to a tag whitelist with http(s)/mailto hrefs only; LeadDetailRecordActivity.vue binds the formatter, adds a formatting hint + live preview in the note modal and the :deep styles\u000a",
        "date" : "2026-09-12 20:08:10 +0200",
        "id" : "9a43b22bbf9db5090902c079f1d6bcf0c76fca38",
        "msg" : "Lead activities: rich rendering of activity notes — new app/utils/activityFormat.ts (escape → linkify → decorate; safe for v-html) renders line-initial / '·'-separated 'Label:' as bold, 'Label (…): https://…' lines as a labelled link chip (e.g. the online confirmation / signing link) instead of the raw token URL, URLs and e-mail addresses as links, file names (.pdf/.docx/…) as badges, **bold**/_italic_ markers, blank lines as paragraph gaps; notes written as simple HTML (<b>, <i>, <u>, <a>, lists, <br>) are sanitised to a tag whitelist with http(s)/mailto hrefs only; LeadDetailRecordActivity.vue binds the formatter, adds a formatting hint + live preview in the note modal and the :deep styles",
        "paths" : [
          {
            "editType" : "add",
            "file" : "app/utils/activityFormat.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/admin/LeadDetailRecordActivity.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/utils/offers/contractPackage.ts",
          "app/pages/sepa/[token].vue",
          "app/components/offers/SigningLinksPanel.vue",
          "app/components/offers/SepaMandateModal.vue",
          "app/pages/partners/partners/[id]/edit.vue",
          "app/composables/useOfferContract.ts",
          "server/api/public/sepa/[token]/document/[key].get.ts",
          "app/utils/iban.ts",
          "server/utils/offers/sepaTexts.ts",
          ".env-dev",
          "nuxt.config.ts",
          "server/utils/offers/sepaMandate.ts",
          "server/api/offers/sepa/preview.post.ts",
          "app/pages/users/leads/[id]/edit.vue",
          "server/utils/offers/sepaPdf.ts",
          "server/api/public/sign/[token]/index.get.ts",
          "app/assets/langs/shared.js",
          "server/api/offers/contract/send.post.ts",
          ".env",
          "app/pages/sign/[token].vue",
          "server/utils/offers/contractSigning.ts",
          "server/api/public/sign/[token]/index.post.ts",
          "server/api/offers/contract/signing-status.get.ts",
          "server/api/public/sepa/[token]/index.get.ts",
          "server/api/public/sepa/[token]/request-renewal.post.ts",
          "server/api/public/sepa/[token]/index.post.ts",
          "app/components/offers/ContractModal.vue",
          "server/api/offers/sepa/send.post.ts",
          ".env-prod",
          "server/utils/offers/contractSigningDb.ts"
        ],
        "commitId" : "0a58e7b9aa9f7f3a6ab93112d2d85788ab0b62e4",
        "timestamp" : 1789237810000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "SEPA-Lastschriftmandat: opt-in contract annex + standalone send/fill/sign flow — new pdf-lib mandate (server/utils/offers/sepaPdf.ts: A4, branded, native fillable AcroForm fields for company/account holder/address/IBAN/BIC/bank/place-date, signed variant sets values, draws the signature, adds a protocol line and flattens), DE/EN mandate wording per DK/EPC (sepaTexts.ts: CORE with 8-week refund notice / B2B without, pre-notification line, 36-month lapse), IBAN validation in app/utils/iban.ts (ISO 13616 per-country lengths, MOD 97-10, SEPA country set, BIC check) used live in the portals and re-checked server-side; (1) contract package: 'Anlage 4 — SEPA-Lastschriftmandat' checkbox in ContractModal (default off, scheme B2B/CORE + payment type + pre-notification days), assembled by contractPackage.ts, mandate stored on the signing row, /sign portal gets step 1b (account holder, IBAN, BIC, bank) + two mandate declarations, finalizeSignature renders the filled+signed mandate, attaches it to the record and patches offer_conditions.sepa (masked IBAN); (2) standalone: SepaMandateModal ('SEPA' button on the partner and lead pages, DE default/EN, prefill from partner contacts + location, preview) → POST /api/offers/sepa/send (signing kind 'sepa', mail with /sepa/<token>, blank mandate attached to the record, activity) → public /sepa/<token> portal + routes (fill, validate, sign) → finalizeSepaMandate attaches the signed PDF to the sending C_BPartner / lead user, logs an activity, mails customer + info@; signing-status + SigningLinksPanel know kind 'sepa' (panel now also on the partner page); runtimeConfig.sepa + SEPA_CREDITOR_ID/SEPA_PRENOTIFICATION_DAYS env keys (creditor id still to be filled in)\u000a",
        "date" : "2026-09-12 20:30:10 +0200",
        "id" : "0a58e7b9aa9f7f3a6ab93112d2d85788ab0b62e4",
        "msg" : "SEPA-Lastschriftmandat: opt-in contract annex + standalone send/fill/sign flow — new pdf-lib mandate (server/utils/offers/sepaPdf.ts: A4, branded, native fillable AcroForm fields for company/account holder/address/IBAN/BIC/bank/place-date, signed variant sets values, draws the signature, adds a protocol line and flattens), DE/EN mandate wording per DK/EPC (sepaTexts.ts: CORE with 8-week refund notice / B2B without, pre-notification line, 36-month lapse), IBAN validation in app/utils/iban.ts (ISO 13616 per-country lengths, MOD 97-10, SEPA country set, BIC check) used live in the portals and re-checked server-side; (1) contract package: 'Anlage 4 — SEPA-Lastschriftmandat' checkbox in ContractModal (default off, scheme B2B/CORE + payment type + pre-notification days), assembled by contractPackage.ts, mandate stored on the signing row, /sign portal gets step 1b (account holder, IBAN, BIC, bank) + two mandate declarations, finalizeSignature renders the filled+signed mandate, attaches it to the record and patches offer_conditions.sepa (masked IBAN); (2) standalone: SepaMandateModal ('SEPA' button on the partner and lead pages, DE default/EN, prefill from partner contacts + location, preview) → POST /api/offers/sepa/send (signing kind 'sepa', mail with /sepa/<token>, blank mandate attached to the record, activity) → public /sepa/<token> portal + routes (fill, validate, sign) → finalizeSepaMandate attaches the signed PDF to the sending C_BPartner / lead user, logs an activity, mails customer + info@; signing-status + SigningLinksPanel know kind 'sepa' (panel now also on the partner page); runtimeConfig.sepa + SEPA_CREDITOR_ID/SEPA_PRENOTIFICATION_DAYS env keys (creditor id still to be filled in)",
        "paths" : [
          {
            "editType" : "add",
            "file" : "server/api/public/sepa/[token]/index.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/offers/contractPackage.ts"
          },
          {
            "editType" : "add",
            "file" : "app/components/offers/SepaMandateModal.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/api/public/sign/[token]/index.post.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env-dev"
          },
          {
            "editType" : "edit",
            "file" : ".env"
          },
          {
            "editType" : "edit",
            "file" : "nuxt.config.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/offers/sepa/preview.post.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/offers/sepaPdf.ts"
          },
          {
            "editType" : "add",
            "file" : "app/utils/iban.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/public/sepa/[token]/request-renewal.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/api/public/sign/[token]/index.get.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/public/sepa/[token]/document/[key].get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "app/components/offers/ContractModal.vue"
          },
          {
            "editType" : "edit",
            "file" : ".env-prod"
          },
          {
            "editType" : "add",
            "file" : "app/pages/sepa/[token].vue"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/offers/contractSigningDb.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/composables/useOfferContract.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/offers/sepa/send.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/partners/partners/[id]/edit.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/api/offers/contract/signing-status.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/offers/SigningLinksPanel.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/leads/[id]/edit.vue"
          },
          {
            "editType" : "add",
            "file" : "server/utils/offers/sepaMandate.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/offers/sepaTexts.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/api/offers/contract/send.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/offers/contractSigning.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/sign/[token].vue"
          },
          {
            "editType" : "add",
            "file" : "server/api/public/sepa/[token]/index.post.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "reference/recipes.md"
        ],
        "commitId" : "1cb4bf0a8ddba51fba801514f9cb3269bbca259c",
        "timestamp" : 1789238308000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Recipes: click-to-dial (FreePBX AMI, <DialButton>, PBX gotchas) and SEPA mandate (contract annex + standalone portal, IBAN check, creditor id)\u000a",
        "date" : "2026-09-12 20:38:28 +0200",
        "id" : "1cb4bf0a8ddba51fba801514f9cb3269bbca259c",
        "msg" : "Recipes: click-to-dial (FreePBX AMI, <DialButton>, PBX gotchas) and SEPA mandate (contract annex + standalone portal, IBAN check, creditor id)",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "reference/recipes.md"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/api/admin/users/update.put.ts",
          "app/components/dashboard/ProfileForm.vue",
          "app/pages/users/profile/index.vue"
        ],
        "commitId" : "a419d4d00e539ecd68e844a02e2fcbf3944828b9",
        "timestamp" : 1789239065000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Profile: system users can set their own SIP extension (click-to-dial) on /users/profile → Contact tab — field shown only for IsSystemUser records, saved via the profile's existing PUT; update.put.ts accepts sipExtension for the caller's own record (JWT AD_User_ID = body.id) in addition to verified admins\u000a",
        "date" : "2026-09-12 20:51:05 +0200",
        "id" : "a419d4d00e539ecd68e844a02e2fcbf3944828b9",
        "msg" : "Profile: system users can set their own SIP extension (click-to-dial) on /users/profile → Contact tab — field shown only for IsSystemUser records, saved via the profile's existing PUT; update.put.ts accepts sipExtension for the caller's own record (JWT AD_User_ID = body.id) in addition to verified admins",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "server/api/admin/users/update.put.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/ProfileForm.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/profile/index.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/NewNavBar.vue",
          "app/assets/langs/shared.js",
          "app/components/QuickDial.vue"
        ],
        "commitId" : "9e9dac38e431a6bed27c32b76c6319179633d87d",
        "timestamp" : 1789239210000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Top bar quick dial (admins with a SIP extension): number input left of the user menu — Enter dials with the own extension, the dial button opens the extension chooser; hidden for non-admins and users who cannot dial; error-boundaried like the chat badge\u000a",
        "date" : "2026-09-12 20:53:30 +0200",
        "id" : "9e9dac38e431a6bed27c32b76c6319179633d87d",
        "msg" : "Top bar quick dial (admins with a SIP extension): number input left of the user menu — Enter dials with the own extension, the dial button opens the extension chooser; hidden for non-admins and users who cannot dial; error-boundaried like the chat badge",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "app/components/NewNavBar.vue"
          },
          {
            "editType" : "add",
            "file" : "app/components/QuickDial.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/dashboard/RecentCallsCard.vue",
          "package-lock.json",
          "app/pages/index.vue",
          "app/components/TopMegaBar.vue",
          ".env",
          "server/api/telephony/recent-calls.get.ts",
          ".env-dev",
          "nuxt.config.ts",
          "server/utils/telephony/cdr.ts",
          "package.json",
          "reference/recipes.md",
          "app/assets/langs/shared.js",
          ".env-prod"
        ],
        "commitId" : "591fe6109509809117dc5b97cb1136ef33ba8472",
        "timestamp" : 1789240092000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Dashboard 'Letzte Anrufe' card (admin roles) + quick dial placed in the top bar — new GET /api/telephony/recent-calls reads the FreePBX CDR via a read-only MariaDB user (mysql2, PBX_CDR_* env) and reduces the many per-leg cdr rows to ONE entry per call (grouped by linkedid; inbound/outbound by trunk channel, answered = ANSWERED Dial leg with billsec>0 to a device), showing the last 20 missed inbound + successful outbound calls (all/in/out filter, missed badge, extension + duration, auto-refresh 60 s, 30 s server cache); numbers are clickable (click-to-dial with the own extension) plus a chooser button per row; QuickDial moved into the TopMegaBar user-menu container (the app layout uses TopMegaBar, not NewNavBar) so it renders left of the user menu; caller-id names stripped of the PBX 'LogYou:' / 'Anruf:' prefixes; recipe note\u000a",
        "date" : "2026-09-12 21:08:12 +0200",
        "id" : "591fe6109509809117dc5b97cb1136ef33ba8472",
        "msg" : "Dashboard 'Letzte Anrufe' card (admin roles) + quick dial placed in the top bar — new GET /api/telephony/recent-calls reads the FreePBX CDR via a read-only MariaDB user (mysql2, PBX_CDR_* env) and reduces the many per-leg cdr rows to ONE entry per call (grouped by linkedid; inbound/outbound by trunk channel, answered = ANSWERED Dial leg with billsec>0 to a device), showing the last 20 missed inbound + successful outbound calls (all/in/out filter, missed badge, extension + duration, auto-refresh 60 s, 30 s server cache); numbers are clickable (click-to-dial with the own extension) plus a chooser button per row; QuickDial moved into the TopMegaBar user-menu container (the app layout uses TopMegaBar, not NewNavBar) so it renders left of the user menu; caller-id names stripped of the PBX 'LogYou:' / 'Anruf:' prefixes; recipe note",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "reference/recipes.md"
          },
          {
            "editType" : "edit",
            "file" : "app/components/TopMegaBar.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "package.json"
          },
          {
            "editType" : "edit",
            "file" : ".env"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/recent-calls.get.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env-dev"
          },
          {
            "editType" : "edit",
            "file" : ".env-prod"
          },
          {
            "editType" : "edit",
            "file" : "package-lock.json"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/cdr.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "nuxt.config.ts"
          },
          {
            "editType" : "add",
            "file" : "app/components/dashboard/RecentCallsCard.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/dashboard/RecentCallsCard.vue",
          "server/api/telephony/match-contacts.post.ts",
          "app/components/DialButton.vue",
          "app/pages/index.vue"
        ],
        "commitId" : "2f23a38d108cdab644854df9215c2bc74095ea38",
        "timestamp" : 1789250857000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Dashboard: first row now Open Orders (0.9fr, ≈−10 %) · tickets (0.6fr, ≈−40 %) · recent-calls card (0.7fr) side by side; tickets fullscreen button commented out for now; recent-calls card limited to the last 10 calls, clicking a number opens the dial popover (own extension from the profile preselected, call starts only after 'Anrufen') instead of dialing immediately, and phone numbers are matched to ERP contacts asynchronously after render via new POST /api/telephony/match-contacts (Elasticsearch ad_user index, regexp on the keyword phone/phone2 fields, fail-soft) — matched names shown with a contact icon and passed as the activity target; DialButton gains a default slot to use custom trigger content (the number text)\u000a",
        "date" : "2026-09-13 00:07:37 +0200",
        "id" : "2f23a38d108cdab644854df9215c2bc74095ea38",
        "msg" : "Dashboard: first row now Open Orders (0.9fr, ≈−10 %) · tickets (0.6fr, ≈−40 %) · recent-calls card (0.7fr) side by side; tickets fullscreen button commented out for now; recent-calls card limited to the last 10 calls, clicking a number opens the dial popover (own extension from the profile preselected, call starts only after 'Anrufen') instead of dialing immediately, and phone numbers are matched to ERP contacts asynchronously after render via new POST /api/telephony/match-contacts (Elasticsearch ad_user index, regexp on the keyword phone/phone2 fields, fail-soft) — matched names shown with a contact icon and passed as the activity target; DialButton gains a default slot to use custom trigger content (the number text)",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/RecentCallsCard.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/index.vue"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/match-contacts.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/DialButton.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/pages/demo/set-builder.vue",
          "app/pages/demo/index.vue"
        ],
        "commitId" : "57d1db967c449a684860b40699282d50920ca919",
        "timestamp" : 1789252825000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Demo screens for the Stücklisten/Set feature (app/pages/demo)\u000a\u000a/demo hub lists real IsBOM articles; /demo/set-builder is a Set-Builder template in quick-order positions style with real product search, component quantities and stock, set image proposals from component photos, and an order-import preview. Public route, no backend writes. Used for logyou.de/leistungen/stuecklisten-bom.\u000a\u000aCo-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>\u000aClaude-Session: https://claude.ai/code/session_01CdyiVTWYfCkMzH8rJyc5Pq\u000a",
        "date" : "2026-09-13 00:40:25 +0200",
        "id" : "57d1db967c449a684860b40699282d50920ca919",
        "msg" : "Demo screens for the Stücklisten/Set feature (app/pages/demo)",
        "paths" : [
          {
            "editType" : "add",
            "file" : "app/pages/demo/set-builder.vue"
          },
          {
            "editType" : "add",
            "file" : "app/pages/demo/index.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/pages/index.vue",
          "app/components/dashboard/RecentCallsCard.vue",
          "server/api/telephony/match-contacts.post.ts"
        ],
        "commitId" : "75a8a5218182d36766c28dd6046e1320a158e6f1",
        "timestamp" : 1789253014000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Dashboard call card + first row: uniform column starts (the slot-trigger DialButton was stretched/centered by the flex column → left-aligned, fixed 96px meta column with right-aligned duration/missed), slim low headers on the whole first row (title only, icon 30px; the subtitles move into a header tooltip — calls card tooltip = title / blank line / subtitle / missed count), tickets card header now just 'Tickets', call date shown as Heute / Gestern / dd.MM.yy without time (full weekday + date + time as hover tooltip), matched contact name is now a button opening a teleported menu 'Als Lead öffnen / Als Benutzer öffnen' (+ 'Partner bearbeiten / ansehen' when a C_BPartner is linked) — match-contacts enriches the ES hit with IsSalesLead/IsVendorLead + C_BPartner_ID name from iDempiere (fail-soft) so the known record kind is listed first\u000a",
        "date" : "2026-09-13 00:43:34 +0200",
        "id" : "75a8a5218182d36766c28dd6046e1320a158e6f1",
        "msg" : "Dashboard call card + first row: uniform column starts (the slot-trigger DialButton was stretched/centered by the flex column → left-aligned, fixed 96px meta column with right-aligned duration/missed), slim low headers on the whole first row (title only, icon 30px; the subtitles move into a header tooltip — calls card tooltip = title / blank line / subtitle / missed count), tickets card header now just 'Tickets', call date shown as Heute / Gestern / dd.MM.yy without time (full weekday + date + time as hover tooltip), matched contact name is now a button opening a teleported menu 'Als Lead öffnen / Als Benutzer öffnen' (+ 'Partner bearbeiten / ansehen' when a C_BPartner is linked) — match-contacts enriches the ES hit with IsSalesLead/IsVendorLead + C_BPartner_ID name from iDempiere (fail-soft) so the known record kind is listed first",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/pages/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/api/telephony/match-contacts.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/RecentCallsCard.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          ".gitignore",
          "server/api/telephony/recordings/transcribe.post.ts",
          "server/api/telephony/recordings/index.get.ts",
          ".env",
          "server/api/telephony/recordings/audio.get.ts",
          "server/utils/telephony/recordings.ts",
          ".env-dev",
          "nuxt.config.ts",
          "server/utils/telephony/cdr.ts",
          "app/pages/users/leads/[id]/edit.vue",
          "server/utils/chatTranscribe.ts",
          "reference/recipes.md",
          "app/components/telephony/CallRecordingsModal.vue",
          ".env-prod",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
        "timestamp" : 1789253031000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Lead page 'Aufnahmen': call recordings + whisper transcription — new components/telephony/CallRecordingsModal.vue (button + CTA on /edit/lead-<id>): step 1 lists every FreePBX recording matched by the lead's phone numbers (all pre-selected, single rows can be unticked, in-app audio player + download per row, language select DE/EN/ES/auto), 'Transkribieren' runs the selected ones one after the other through the server whisper.cpp pipeline with real progress, step 2 shows the transcripts to read (optional timestamps), copy and download (single / all as one .txt). Server: server/utils/telephony/recordings.ts (CDR lookup recordingfile<>'' + src/dst suffix match, one row per file, stat on the PBX drops missing/44-byte never-answered files, transcript cache data/call-transcripts/<file>.json, serial whisper queue, timeout 3× audio length), routes telephony/recordings/{index.get,transcribe.post,audio.get} gated by gateFromEvent (staff), chatTranscribe.ts gains transcribeAudioDetailed() (text + timestamped segments, timeout/extra-args options; transcribeAudio() is the wrapper) and no longer converts a .wav onto itself (ffmpeg refused in-place edits); PBX side: /opt/logship-ivr/recordings.js on 192.168.12.73 (systemd logship-recordings, port 8096, LAN allow-list + X-IVR-Secret = IVR_SHARED_SECRET, POST /recordings/stat because Node 8 caps headers at 8 KB, GET /recordings/file/<name>, read-only); runtimeConfig.pbxRecordings + PBX_RECORDINGS_URL env; lead page now defines t (useTranslation) which the SEPA title already referenced; rec_* translations en/de; recipe\u000a",
        "date" : "2026-09-13 00:43:51 +0200",
        "id" : "d626e2ef330acc638feb470c8d8c8d79fe8c6845",
        "msg" : "Lead page 'Aufnahmen': call recordings + whisper transcription — new components/telephony/CallRecordingsModal.vue (button + CTA on /edit/lead-<id>): step 1 lists every FreePBX recording matched by the lead's phone numbers (all pre-selected, single rows can be unticked, in-app audio player + download per row, language select DE/EN/ES/auto), 'Transkribieren' runs the selected ones one after the other through the server whisper.cpp pipeline with real progress, step 2 shows the transcripts to read (optional timestamps), copy and download (single / all as one .txt). Server: server/utils/telephony/recordings.ts (CDR lookup recordingfile<>'' + src/dst suffix match, one row per file, stat on the PBX drops missing/44-byte never-answered files, transcript cache data/call-transcripts/<file>.json, serial whisper queue, timeout 3× audio length), routes telephony/recordings/{index.get,transcribe.post,audio.get} gated by gateFromEvent (staff), chatTranscribe.ts gains transcribeAudioDetailed() (text + timestamped segments, timeout/extra-args options; transcribeAudio() is the wrapper) and no longer converts a .wav onto itself (ffmpeg refused in-place edits); PBX side: /opt/logship-ivr/recordings.js on 192.168.12.73 (systemd logship-recordings, port 8096, LAN allow-list + X-IVR-Secret = IVR_SHARED_SECRET, POST /recordings/stat because Node 8 caps headers at 8 KB, GET /recordings/file/<name>, read-only); runtimeConfig.pbxRecordings + PBX_RECORDINGS_URL env; lead page now defines t (useTranslation) which the SEPA title already referenced; rec_* translations en/de; recipe",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "reference/recipes.md"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/users/leads/[id]/edit.vue"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/recordings/index.get.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/recordings.ts"
          },
          {
            "editType" : "edit",
            "file" : ".gitignore"
          },
          {
            "editType" : "edit",
            "file" : ".env-dev"
          },
          {
            "editType" : "add",
            "file" : "app/components/telephony/CallRecordingsModal.vue"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/recordings/audio.get.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env"
          },
          {
            "editType" : "edit",
            "file" : "nuxt.config.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/chatTranscribe.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env-prod"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/recordings/transcribe.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/telephony/cdr.ts"
          }
        ]
      }
    ],
    "kind" : "git"
  },
  "culprits" : [
    {
      "absoluteUrl" : "https://jenkins.younex.de/user/info",
      "fullName" : "info"
    }
  ]
}