{
  "_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" : 737,
          "buildResult" : None,
          "marked" : {
            "SHA1" : "7ccda3abf09f752e54f2ad7299b242330511d169",
            "branch" : [
              {
                "SHA1" : "7ccda3abf09f752e54f2ad7299b242330511d169",
                "name" : "refs/remotes/origin/dev"
              }
            ]
          },
          "revision" : {
            "SHA1" : "7ccda3abf09f752e54f2ad7299b242330511d169",
            "branch" : [
              {
                "SHA1" : "7ccda3abf09f752e54f2ad7299b242330511d169",
                "name" : "refs/remotes/origin/dev"
              }
            ]
          }
        }
      },
      "lastBuiltRevision" : {
        "SHA1" : "7ccda3abf09f752e54f2ad7299b242330511d169",
        "branch" : [
          {
            "SHA1" : "7ccda3abf09f752e54f2ad7299b242330511d169",
            "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" : None,
  "displayName" : "#737",
  "duration" : 71493,
  "estimatedDuration" : 71602,
  "executor" : None,
  "fullDisplayName" : "build-logship-frontend #737",
  "id" : "737",
  "inProgress" : False,
  "keepLog" : False,
  "number" : 737,
  "queueId" : 5701,
  "result" : "SUCCESS",
  "timestamp" : 1789430400272,
  "url" : "https://jenkins.younex.de/job/build-logship-frontend/737/",
  "builtOn" : "",
  "changeSet" : {
    "_class" : "hudson.plugins.git.GitChangeSetList",
    "items" : [
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/utils/telephony/extensions.ts",
          "app/composables/useClickToDial.ts",
          "app/components/DialButton.vue"
        ],
        "commitId" : "630fc88a6a26eba7608728a395bb5fc80a4964ef",
        "timestamp" : 1789370185000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Dial popover: show the PBX extension name instead of the state text — server/utils/telephony/extensions.ts reads each extension's FreePBX display name from the astdb (AMI DBGet AMPUSER/<ext>/cidname, covered by the reporting class already granted to the logship manager user; cached 10 min per extension, fetched on the same socket as DeviceStateList, ERP user name stays the fallback), DialButton rows now read '401 Younes' with the state carried by the coloured dot + hover tooltip only, DirectoryExtension type gains pbxName\u000a",
        "date" : "2026-09-14 09:16:25 +0200",
        "id" : "630fc88a6a26eba7608728a395bb5fc80a4964ef",
        "msg" : "Dial popover: show the PBX extension name instead of the state text — server/utils/telephony/extensions.ts reads each extension's FreePBX display name from the astdb (AMI DBGet AMPUSER/<ext>/cidname, covered by the reporting class already granted to the logship manager user; cached 10 min per extension, fetched on the same socket as DeviceStateList, ERP user name stays the fallback), DialButton rows now read '401 Younes' with the state carried by the coloured dot + hover tooltip only, DirectoryExtension type gains pbxName",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "server/utils/telephony/extensions.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/DialButton.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/composables/useClickToDial.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/accounting/banking/BankBackfillPanel.vue",
          "server/api/accounting/bank-transactions/coverage.get.ts",
          "app/assets/css/banking.css",
          "app/components/MainNavigationMenu.vue",
          "app/pages/accounting/banking/index.vue",
          "app/composables/menu/admin/accounting.ts",
          "app/assets/langs/shared.js",
          "app/pages/accounting/bank-transactions/index.vue"
        ],
        "commitId" : "53342fb748940c080a9c541b0d4c1a87499d5339",
        "timestamp" : 1789372209000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Bank fetch page: /accounting/bank-transactions becomes the dedicated \"Bankabruf\" page (fetch from the bank), replacing the duplicate staged-statements/import tables — coverage table per connection (data from/to, counts, last fetch, 12-month strip green = staged / red = never fetched, click = preselect), backfill runner for past periods (months from a coverage-aware grid with \"Select missing\", everything since a date, free range, single day; chunks of max 31 days so PayPal's 30-day window and the proxy timeout are respected; sequential per connection + chunk, progress bar, per-chunk log, abort, retry failed, details via the fetch-result modal, dry-run toggle, hand-off to Banking → import), connection cards kept in a collapsible section; new proxy route bank-transactions/coverage.get.ts (Laravel banking/statements/coverage, fail-soft notice when not deployed); shared bk-* styles moved from the Banking page into app/assets/css/banking.css (loaded by both pages); Banking fetch modal links to the backfill page; menu labels \"Bankabruf\"/\"Bank Fetch\"; bankfetch_* translations en/de/es\u000a",
        "date" : "2026-09-14 09:50:09 +0200",
        "id" : "53342fb748940c080a9c541b0d4c1a87499d5339",
        "msg" : "Bank fetch page: /accounting/bank-transactions becomes the dedicated \"Bankabruf\" page (fetch from the bank), replacing the duplicate staged-statements/import tables — coverage table per connection (data from/to, counts, last fetch, 12-month strip green = staged / red = never fetched, click = preselect), backfill runner for past periods (months from a coverage-aware grid with \"Select missing\", everything since a date, free range, single day; chunks of max 31 days so PayPal's 30-day window and the proxy timeout are respected; sequential per connection + chunk, progress bar, per-chunk log, abort, retry failed, details via the fetch-result modal, dry-run toggle, hand-off to Banking → import), connection cards kept in a collapsible section; new proxy route bank-transactions/coverage.get.ts (Laravel banking/statements/coverage, fail-soft notice when not deployed); shared bk-* styles moved from the Banking page into app/assets/css/banking.css (loaded by both pages); Banking fetch modal links to the backfill page; menu labels \"Bankabruf\"/\"Bank Fetch\"; bankfetch_* translations en/de/es",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/bank-transactions/coverage.get.ts"
          },
          {
            "editType" : "add",
            "file" : "app/components/accounting/banking/BankBackfillPanel.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/MainNavigationMenu.vue"
          },
          {
            "editType" : "add",
            "file" : "app/assets/css/banking.css"
          },
          {
            "editType" : "edit",
            "file" : "app/composables/menu/admin/accounting.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/accounting/bank-transactions/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/accounting/banking/index.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/dashboard/RecentCallsCard.vue",
          "app/components/dashboard/QuickContactModal.vue",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "f810d698d77298b9a9a8e0a18b8ea93623b496bc",
        "timestamp" : 1789372736000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Dashboard calls card: '+' on unmatched numbers → quick-create contact modal — new components/dashboard/QuickContactModal.vue (teleported; name required + autofocused, phone preset from the call row incl. PBX caller-id name, optional e-mail and 'als Lead anlegen' flag; creates a plain ad_user via /api/admin/users/store, not a system user, current org); RecentCallsCard remembers checked-but-unmatched numbers (null entries) so the '+' only shows after the ES match finished, and stores the created user as the number's contact locally so the row flips to the contact menu without waiting for the ad_user index sync; rcc_add_contact/qcm_* translations en/de/es\u000a",
        "date" : "2026-09-14 09:58:56 +0200",
        "id" : "f810d698d77298b9a9a8e0a18b8ea93623b496bc",
        "msg" : "Dashboard calls card: '+' on unmatched numbers → quick-create contact modal — new components/dashboard/QuickContactModal.vue (teleported; name required + autofocused, phone preset from the call row incl. PBX caller-id name, optional e-mail and 'als Lead anlegen' flag; creates a plain ad_user via /api/admin/users/store, not a system user, current org); RecentCallsCard remembers checked-but-unmatched numbers (null entries) so the '+' only shows after the ES match finished, and stores the created user as the number's contact locally so the row flips to the contact menu without waiting for the ad_user index sync; rcc_add_contact/qcm_* translations en/de/es",
        "paths" : [
          {
            "editType" : "add",
            "file" : "app/components/dashboard/QuickContactModal.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/RecentCallsCard.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/api/commission/parcels/dhl/multi.post.ts",
          "server/api/commission/parcels/dhl/index.post.ts",
          "server/api/commission/parcels/dpd/multi.post.ts",
          "server/api/commission/parcels/dpd/index.post.ts"
        ],
        "commitId" : "e644ded9fd4885ca7e5be18f1d3c638d0b687e12",
        "timestamp" : 1789373944000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "DPD labels: carry address2 (Adresszusatz) like DHL — dpd/index + dpd/multi now map body.address2 into the recipient: name2 when no company/c/o is set, otherwise the company keeps name2 and address2 goes to contact (the only other printed DPD line, 35 chars), plus a data-only comment mirror (70); <contact> emitted after <city> and <comment> after <email> per the DPD XSD order. DHL index/multi cap consignee name2/name3 at 50 chars so over-long values are truncated instead of rejected. Frontend unchanged (commission.vue already sends both fields to both carriers).\u000a",
        "date" : "2026-09-14 10:19:04 +0200",
        "id" : "e644ded9fd4885ca7e5be18f1d3c638d0b687e12",
        "msg" : "DPD labels: carry address2 (Adresszusatz) like DHL — dpd/index + dpd/multi now map body.address2 into the recipient: name2 when no company/c/o is set, otherwise the company keeps name2 and address2 goes to contact (the only other printed DPD line, 35 chars), plus a data-only comment mirror (70); <contact> emitted after <city> and <comment> after <email> per the DPD XSD order. DHL index/multi cap consignee name2/name3 at 50 chars so over-long values are truncated instead of rejected. Frontend unchanged (commission.vue already sends both fields to both carriers).",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "server/api/commission/parcels/dpd/index.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/api/commission/parcels/dhl/index.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/api/commission/parcels/dhl/multi.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/api/commission/parcels/dpd/multi.post.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/utils/telephony/transcriptionResponse.ts",
          "server/api/telephony/recordings/transcribe-status.get.ts",
          "server/api/telephony/recordings/transcribe.post.ts",
          "app/components/telephony/CallRecordingsModal.vue",
          "server/utils/telephony/recordings.ts"
        ],
        "commitId" : "72610850d43102cc0e14ac7cf6ef1b5db716ace6",
        "timestamp" : 1789375913000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Call recordings: async whisper transcription — transcribe.post.ts starts the job, waits max 20 s and answers 202 pending for anything longer (the edge nginx cuts requests at 60 s, long calls used to 504 while whisper kept running); new transcribe-status.get.ts polls the disk state (pending marker <file>.pending.json shared across PM2 instances, cache file = done, marker error = failed); shared statusResponse() in transcriptionResponse.ts; CallRecordingsModal polls every 3 s up to 25 min and shows the elapsed time\u000a",
        "date" : "2026-09-14 10:51:53 +0200",
        "id" : "72610850d43102cc0e14ac7cf6ef1b5db716ace6",
        "msg" : "Call recordings: async whisper transcription — transcribe.post.ts starts the job, waits max 20 s and answers 202 pending for anything longer (the edge nginx cuts requests at 60 s, long calls used to 504 while whisper kept running); new transcribe-status.get.ts polls the disk state (pending marker <file>.pending.json shared across PM2 instances, cache file = done, marker error = failed); shared statusResponse() in transcriptionResponse.ts; CallRecordingsModal polls every 3 s up to 25 min and shows the elapsed time",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "server/api/telephony/recordings/transcribe.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/telephony/CallRecordingsModal.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/telephony/recordings.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/recordings/transcribe-status.get.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/telephony/transcriptionResponse.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "reference/roadsurfer_Fulfillment_Anfrage_Preisblatt_LogYou.xlsx",
          "reference/~$roadsurfer_Fulfillment_Anfrage_Preisblatt_LogYou.xlsx"
        ],
        "commitId" : "14d0e08b8c0a28cd535aeb98b9273a2ce1dfb557",
        "timestamp" : 1789375913000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Roadsurfer price sheet: update workbook, drop stray Excel lock file\u000a",
        "date" : "2026-09-14 10:51:53 +0200",
        "id" : "14d0e08b8c0a28cd535aeb98b9273a2ce1dfb557",
        "msg" : "Roadsurfer price sheet: update workbook, drop stray Excel lock file",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "reference/roadsurfer_Fulfillment_Anfrage_Preisblatt_LogYou.xlsx"
          },
          {
            "editType" : "delete",
            "file" : "reference/~$roadsurfer_Fulfillment_Anfrage_Preisblatt_LogYou.xlsx"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/api/accounting/amazon-ads/connect.get.ts",
          ".gitignore",
          "server/api/accounting/amazon-ads/document.get.ts",
          "server/api/accounting/amazon-ads/import.post.ts",
          "server/api/accounting/amazon-ads/disconnect.post.ts",
          "server/utils/amazonAds/types.ts",
          ".env-dev",
          "nuxt.config.ts",
          "server/api/invoices/lexoffice-upload.post.ts",
          "server/api/accounting/amazon-ads/status.get.ts",
          "server/api/oauth/amazon-ads/callback.get.ts",
          "app/components/AgGrid.vue",
          "app/assets/langs/shared.js",
          "server/utils/amazonAds/routeShared.ts",
          "server/utils/amazonAds/importInvoice.ts",
          "server/utils/amazonAds/parseInvoicePdf.ts",
          "server/utils/amazonAds/adsApi.ts",
          "server/api/accounting/amazon-ads/profile.put.ts",
          ".env",
          "reference/INVOICE-503111M5PA26.pdf",
          "app/pages/accounting/amazon-ads-invoices/index.vue",
          "server/utils/invoiceOriginalPdf.ts",
          "server/api/accounting/amazon-ads/invoices.get.ts",
          "reference/test-scripts/amazon-ads-parse.mjs",
          "server/api/accounting/amazon-ads/upload.post.ts",
          "app/components/MainNavigationMenu.vue",
          "server/api/accounting/amazon-ads/profiles.get.ts",
          "server/utils/amazonAds/adsStore.ts",
          "app/composables/menu/admin/accounting.ts",
          "server/api/accounting/amazon-ads/void.post.ts",
          ".env-prod"
        ],
        "commitId" : "0f630bc593490f322597f096f15bc84851b5a3c0",
        "timestamp" : 1789375913000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Amazon Ads invoices → Eingangsrechnungen: new page /accounting/amazon-ads-invoices lists Sponsored-Ads invoices (Amazon Ads API when connected — LWA consent flow, profile picker, GET /invoices + /invoices/{id} + /billing/documents PDF — or console PDFs dropped on the page and parsed server-side), lets the user tick which ones to import and books each as a completed AP invoice on the fixed vendor 1043801 (org *): one charge line per campaign (default charge Werbekosten), tax by rate so GrandTotal = Amazon gross, DocumentNo/POReference = Amazon invoice no., duplicate guard, original Amazon PDF attached under PO_Invoice; Storno action voids + renames the number (<no>-VOID-<id>) so the same invoice can be re-imported; booked/Lexoffice status comes from iDempiere (vendor AP invoices matched by number). Lexoffice upload: purchase invoices now upload the attached vendor original instead of the Jasper print (no ZUGFeRD on foreign documents) and requireOriginal:true skips invoices without one (used by the Ads page). Store data/amazon-ads.db + PDF cache data/amazon-ads/ (git-ignored); AMAZON_ADS_* env keys (empty = PDF-upload mode until the Ads API approval exists); AgGrid wrapper registers RowGroupingPanelModule (error #200 on every grid page); menu + en/de/es labels; parser dev script reference/test-scripts/amazon-ads-parse.mjs + sample invoice\u000a",
        "date" : "2026-09-14 10:51:53 +0200",
        "id" : "0f630bc593490f322597f096f15bc84851b5a3c0",
        "msg" : "Amazon Ads invoices → Eingangsrechnungen: new page /accounting/amazon-ads-invoices lists Sponsored-Ads invoices (Amazon Ads API when connected — LWA consent flow, profile picker, GET /invoices + /invoices/{id} + /billing/documents PDF — or console PDFs dropped on the page and parsed server-side), lets the user tick which ones to import and books each as a completed AP invoice on the fixed vendor 1043801 (org *): one charge line per campaign (default charge Werbekosten), tax by rate so GrandTotal = Amazon gross, DocumentNo/POReference = Amazon invoice no., duplicate guard, original Amazon PDF attached under PO_Invoice; Storno action voids + renames the number (<no>-VOID-<id>) so the same invoice can be re-imported; booked/Lexoffice status comes from iDempiere (vendor AP invoices matched by number). Lexoffice upload: purchase invoices now upload the attached vendor original instead of the Jasper print (no ZUGFeRD on foreign documents) and requireOriginal:true skips invoices without one (used by the Ads page). Store data/amazon-ads.db + PDF cache data/amazon-ads/ (git-ignored); AMAZON_ADS_* env keys (empty = PDF-upload mode until the Ads API approval exists); AgGrid wrapper registers RowGroupingPanelModule (error #200 on every grid page); menu + en/de/es labels; parser dev script reference/test-scripts/amazon-ads-parse.mjs + sample invoice",
        "paths" : [
          {
            "editType" : "add",
            "file" : "server/utils/amazonAds/parseInvoicePdf.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env-prod"
          },
          {
            "editType" : "edit",
            "file" : "app/components/MainNavigationMenu.vue"
          },
          {
            "editType" : "add",
            "file" : "server/utils/amazonAds/adsApi.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env-dev"
          },
          {
            "editType" : "edit",
            "file" : ".gitignore"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/profiles.get.ts"
          },
          {
            "editType" : "add",
            "file" : "reference/INVOICE-503111M5PA26.pdf"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/disconnect.post.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/void.post.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/import.post.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/invoiceOriginalPdf.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/amazonAds/adsStore.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/connect.get.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/invoices.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "nuxt.config.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/upload.post.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/amazonAds/importInvoice.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/profile.put.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/AgGrid.vue"
          },
          {
            "editType" : "add",
            "file" : "reference/test-scripts/amazon-ads-parse.mjs"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/document.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/api/invoices/lexoffice-upload.post.ts"
          },
          {
            "editType" : "add",
            "file" : "app/pages/accounting/amazon-ads-invoices/index.vue"
          },
          {
            "editType" : "add",
            "file" : "server/utils/amazonAds/routeShared.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/accounting/amazon-ads/status.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/composables/menu/admin/accounting.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/amazonAds/types.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env"
          },
          {
            "editType" : "add",
            "file" : "server/api/oauth/amazon-ads/callback.get.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/dashboard/RecentCallsCard.vue",
          "server/api/admin/users/search.get.ts",
          "app/components/dashboard/QuickContactModal.vue",
          "app/pages/sales/orders/[id]/view.vue",
          "server/utils/telephony/cdr.ts",
          "app/pages/sales/orders/[id]/edit.vue",
          "server/api/telephony/recent-calls.get.ts",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "b99b7ac21f59acb46845cd37905cf64ad2f389e4",
        "timestamp" : 1789378079000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Sales order ship-to name + dashboard calls card improvements\u000a\u000aSales orders: edit.vue only fetched order data in onMounted, so switching\u000abetween order tabs via the in-app tab bar (router.push into the same\u000akeep-alive'd component, only :id changes) kept showing the previously loaded\u000aorder's data — including its business partner — until a hard reload. Added a\u000aroute.params.id watcher (loadOrderData()) mirroring the existing fix on\u000asales/invoices and sales/customer-rma edit pages. Also added the ship-to\u000arecipient name (C_BPartner_Location_ID.Name — the same field the orders grid\u000ashows as \"Name\", which can legitimately differ from the business partner's\u000aown Name) to the read-only order view.vue page, which was missing it\u000aentirely; edit.vue's OrderForm.vue already displayed it correctly.\u000a\u000aDashboard \"Letzte Anrufe\" card: the extension badge and duration/missed\u000alabel were side-by-side in a fixed-width column that could overflow into\u000aneighboring text. Stacked them vertically instead and gave rows a bit more\u000aheight. Added server-side search (number/name) and pagination — cdr.ts's\u000arecentCalls() now takes offset/search/direction and returns total +\u000amissedTotal (so the missed-count badge stays accurate regardless of the\u000aactive direction tab or page).\u000a\u000aQuick-contact modal (dashboard \"+\" on unmatched calls): added a \"search\u000aexisting contact\" panel above the create-new form, so a call can be linked\u000ato an existing ad_user/lead instead of always creating a duplicate. Scoped\u000ato the caller's own organization by default (\"search all organizations\" to\u000awiden), with a type filter (All/Lead/External/Internal) mirroring the\u000asite's existing Users/Leads/External Users split. New\u000aGET /api/admin/users/search endpoint backs it; picking a result only PUTs\u000athe call's phone number onto the existing contact when it isn't already on\u000afile, to avoid clobbering an existing different number.\u000a",
        "date" : "2026-09-14 11:27:59 +0200",
        "id" : "b99b7ac21f59acb46845cd37905cf64ad2f389e4",
        "msg" : "Sales order ship-to name + dashboard calls card improvements",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/pages/sales/orders/[id]/edit.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/sales/orders/[id]/view.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/RecentCallsCard.vue"
          },
          {
            "editType" : "add",
            "file" : "server/api/admin/users/search.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "server/api/telephony/recent-calls.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/telephony/cdr.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/QuickContactModal.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/pages/sales/invoices/index.vue",
          "app/components/sales/InvoiceListFilters.vue",
          "app/components/sales/InvoiceAction.vue",
          "app/components/AgGrid/Invoices/AgInvoiceKind.vue",
          "server/api/invoices/so.get.ts",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "852e5a139e7dc3c8d4e43fa24f46d603c71b710d",
        "timestamp" : 1789379089000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Sales invoices: merge in purchase (AP) invoices, incl. Amazon Ads imports\u000a\u000aserver/api/invoices/so.get.ts dropped its IsSOTrx-only filter (kept the\u000aexisting DocStatus CO/CL/DR restriction) so /sales/invoices now returns both\u000asales and purchase invoices — including the Amazon Ads AP invoices booked via\u000a/accounting/amazon-ads-invoices, which land in c_invoice like any other\u000avendor invoice. Route name/URL kept as-is to avoid touching every caller.\u000a\u000aNew \"Type\" grid column (AgInvoiceKind.vue) tags each row AR/AR-CM/AP/AP-CM\u000afrom C_Invoice.DocBaseType (the same field lexoffice-upload.post.ts already\u000akeys its AR/AP branching on), right after the document-number column.\u000a\u000aInvoiceListFilters.vue gained two segmented filters: Receivables/Payables\u000a(side, derived from IsSOTrx / the C_DocTypeTarget_ID 1000006 \"AP CreditMemo\u000aused as a customer credit note\" exception — same convention as\u000aaccounting/opos) and Lexoffice upload status (uploaded/not uploaded), both\u000aapplied client-side alongside the existing fulfillment/payment/amount\u000afilters so the grand-total row and counts stay consistent.\u000a\u000aThe existing bulk Lexoffice upload button needed no changes — the shared\u000alexoffice-upload.post.ts route already branches on DocBaseType per invoice\u000a(original vendor PDF for AP, Jasper print for AR) and processes mixed AR/AP\u000aid batches correctly; it just never had AP rows to select from before.\u000a\u000aFixed InvoiceAction.vue's row-menu edit/duplicate handlers, which always\u000anavigated to the sales-invoice pages — now they branch on isSOTrx to\u000a/procurements/invoices/{id}/{edit,duplicate} for AP rows, matching the\u000abranching AgLink.vue's document-number link already had.\u000a\u000aNote: this route has no $top cap (pre-existing), so removing the AR-only\u000arestriction roughly doubles the worst-case row count for orgs with a long\u000apurchase-invoice history — worth watching if the page gets sluggish.\u000a",
        "date" : "2026-09-14 11:44:49 +0200",
        "id" : "852e5a139e7dc3c8d4e43fa24f46d603c71b710d",
        "msg" : "Sales invoices: merge in purchase (AP) invoices, incl. Amazon Ads imports",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/pages/sales/invoices/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/api/invoices/so.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "app/components/sales/InvoiceAction.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/sales/InvoiceListFilters.vue"
          },
          {
            "editType" : "add",
            "file" : "app/components/AgGrid/Invoices/AgInvoiceKind.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/pages/users/leads/[id]/edit.vue"
        ],
        "commitId" : "9b08ac127dfe1f98967ced8227f64c07868a1db3",
        "timestamp" : 1789379322000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Lead page: move Aufnahmen button out of the offer/contract CTA group\u000a\u000aThe \"Aufnahmen\" (call recordings) button sat in the top-right CTA cluster\u000aalongside Angebot/Vertrag/SEPA/Onboarding, but it isn't part of that\u000aoffer/contract workflow — it's phone-call history. Moved it next to the\u000aphone number / DialButton in the contact-chips row instead, gated on the\u000alead having a phone number (nothing to match recordings against otherwise).\u000aThe compact top-toolbar icon button (MobileActionsMenu) already had its own\u000a\"Aufnahmen\" entry and is unchanged.\u000a",
        "date" : "2026-09-14 11:48:42 +0200",
        "id" : "9b08ac127dfe1f98967ced8227f64c07868a1db3",
        "msg" : "Lead page: move Aufnahmen button out of the offer/contract CTA group",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/pages/users/leads/[id]/edit.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/dashboard/RecentCallsCard.vue",
          "app/components/DialButton.vue",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "39f1dfd5739eef2e6173ff543b20fba47bca5fa3",
        "timestamp" : 1789379675000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "DialButton: opt-in Recordings toggle (play-only) in the dial popover\u000a\u000aAdds a showRecordings prop that puts a headset icon in the popover header\u000anext to Close. Toggling it switches the popover body to a compact list of\u000aFreePBX recordings matched to `number` (GET /api/telephony/recordings, same\u000aendpoint CallRecordingsModal.vue uses on contact pages, called here with\u000ajust `numbers` — no source/recordId, so it works for numbers with no known\u000acontact too) — date, direction, duration and a play button only, no\u000atranscription/download clutter. Off by default everywhere; enabled on both\u000aDialButton usages in the dashboard's \"Letzte Anrufe\" card, so clicking a\u000acall's number now offers a quick way to listen to past recordings for that\u000anumber right from the dial popover.\u000a",
        "date" : "2026-09-14 11:54:35 +0200",
        "id" : "39f1dfd5739eef2e6173ff543b20fba47bca5fa3",
        "msg" : "DialButton: opt-in Recordings toggle (play-only) in the dial popover",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/RecentCallsCard.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/DialButton.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/dashboard/RecentCallsCard.vue"
        ],
        "commitId" : "3a2aa7e776b1ecd670ff8e1f0abb8a4de7311b04",
        "timestamp" : 1789379818000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Recent calls card: stack date above number, give the name its own full line\u000a\u000aThe date used to be its own fixed 62px grid column, squeezing how much\u000awidth the number/name column got. Moved it to sit above the number instead\u000a(inside the same column), so the name — which can run considerably longer\u000athan the number — gets the full row width on its own line rather than\u000asharing it with a separate date column.\u000a",
        "date" : "2026-09-14 11:56:58 +0200",
        "id" : "3a2aa7e776b1ecd670ff8e1f0abb8a4de7311b04",
        "msg" : "Recent calls card: stack date above number, give the name its own full line",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/components/dashboard/RecentCallsCard.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/api/materials/locators/priority-recommendations.get.ts"
        ],
        "commitId" : "d9627b7266e6432d9febb322babb26fcc4f62970",
        "timestamp" : 1789380713000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Locator priorities: exclude locators not allowed for shipping\u000a\u000aM_LocatorType.IsAvailableForShipping=false locators (return bins,\u000aquarantine, reconditioning input, ...) are dropped before building\u000apick-order conflicts/proposals, since shipment generation never picks\u000afrom them anyway.\u000a",
        "date" : "2026-09-14 12:11:53 +0200",
        "id" : "d9627b7266e6432d9febb322babb26fcc4f62970",
        "msg" : "Locator priorities: exclude locators not allowed for shipping",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "server/api/materials/locators/priority-recommendations.get.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/DialButton.vue",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "c1fb76e4b48ea72b198182db778fce1fb2a4892b",
        "timestamp" : 1789383931000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "DialButton: fix recordings that appeared to play but stayed silent\u000a\u000aChrome's <audio> element does not behave like a normal failed request when\u000athe recordings route errors out (e.g. the PBX recordings service returns a\u000aJSON error body instead of audio): no 'error' event ever fires and\u000a.play()'s promise never settles either — it just sits at\u000anetworkState=LOADING/readyState=HAVE_NOTHING with paused=false forever.\u000aSince the row's play/stop icon was driven only by a manually-toggled ref,\u000aa failed load looked exactly like \"still playing\" with no sound and no\u000away to tell it failed. Add a stall-timeout guard (6s) that aborts the\u000ahung request, resets the button and shows an inline error message when\u000aplayable data never arrives; canplay/playing clear the timer on success.\u000a",
        "date" : "2026-09-14 13:05:31 +0200",
        "id" : "c1fb76e4b48ea72b198182db778fce1fb2a4892b",
        "msg" : "DialButton: fix recordings that appeared to play but stayed silent",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/components/DialButton.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/AgGrid/Invoices/AgInvoiceKind.vue"
        ],
        "commitId" : "182290a4e2c2f6c96174f4a55e9d7c30acf5855c",
        "timestamp" : 1789383938000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "AgInvoiceKind: fix AP/AR tags never actually getting their colors\u000a\u000aThe tag colors never rendered: this component is used as an AG Grid\u000acellRenderer, and its <style scoped> block never reached the rendered\u000aelements (they carried no data-v-* attribute at all), so every tag\u000asilently fell back to Bulma's plain grey .tag look regardless of AR/AP/\u000acredit-memo type — the same gap already documented for AG Grid image\u000acell renderers. Move the styling to a non-scoped block keyed off a\u000adedicated .ag-ikind-tag marker class (so it can't leak onto the many\u000aother Bulma .tag elements elsewhere in the app), and switch from thin\u000aoutlined tags to solid-filled ones so AP vs AR reads at a glance while\u000ascanning a long invoice grid.\u000a",
        "date" : "2026-09-14 13:05:38 +0200",
        "id" : "182290a4e2c2f6c96174f4a55e9d7c30acf5855c",
        "msg" : "AgInvoiceKind: fix AP/AR tags never actually getting their colors",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/components/AgGrid/Invoices/AgInvoiceKind.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/api/invoices/lexoffice-upload.post.ts",
          "app/components/sales/LexofficeCategoryModal.vue"
        ],
        "commitId" : "3648fbba0e22b85d8209ea8ec5b961f204aff7e9",
        "timestamp" : 1789383946000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Lexoffice upload: default AP invoices into Ausgaben, not the general inbox\u000a\u000aWithout an explicit category, uploads went through Lexoffice's plain\u000aFiles-drop, which auto-creates its own uncategorized draft voucher — it\u000anever lands as a filed Einnahmen/Ausgaben entry, just a general/pending\u000aitem in Lexoffice's own inbox. AR invoices only looked correctly filed\u000aunder Einnahmen because someone picked a category for them every time,\u000awhich goes through the Vouchers API instead.\u000a\u000aAdd a third modal option, \"Mit Standardkategorie hochladen\": books a\u000areal voucher via the same Vouchers API as an explicit category, using a\u000ageneric catch-all per direction (Einnahmen for AR/AR-CM, Sonstige\u000aAusgaben for AP/AP-CM) resolved per invoice so a mixed bulk upload still\u000afiles each one correctly. The true \"Ohne Kategorie hochladen\" (plain,\u000auncategorized Files-drop) option is unchanged and still available.\u000a\u000aAlso fixes APC (AP credit memo) incorrectly booking as voucher type\u000apurchaseinvoice instead of purchasecreditnote.\u000a",
        "date" : "2026-09-14 13:05:46 +0200",
        "id" : "3648fbba0e22b85d8209ea8ec5b961f204aff7e9",
        "msg" : "Lexoffice upload: default AP invoices into Ausgaben, not the general inbox",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/components/sales/LexofficeCategoryModal.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/api/invoices/lexoffice-upload.post.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/assets/langs/shared.js",
          "app/pages/accounting/bank-transactions/index.vue"
        ],
        "commitId" : "d7464e740863297d158a64b96ec8573d0e755371",
        "timestamp" : 1789391406000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Bank fetch coverage: show import progress per month, not just staged\u000a\u000aCoverage cells only reflected whether any statement was staged for a\u000amonth; add the imported/open counts the coverage endpoint already\u000areturns so a month reads green only once fully imported, amber while\u000apartially imported, with the count shown. Also surface the imported\u000acount as a pill on the connection row, next to the existing pending one.\u000a",
        "date" : "2026-09-14 15:10:06 +0200",
        "id" : "d7464e740863297d158a64b96ec8573d0e755371",
        "msg" : "Bank fetch coverage: show import progress per month, not just staged",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/accounting/bank-transactions/index.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          ".env-dev",
          "nuxt.config.ts",
          ".gitignore",
          "server/api/telephony/cid-lookup/[number].get.ts",
          "server/utils/elasticHelper.ts",
          ".env",
          ".env-prod"
        ],
        "commitId" : "3c67c7ed036f479cad2a91044c687bb5dbfbe6a4",
        "timestamp" : 1789399518000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Add asterisk lookup endpoint\u000a\u000aSquashed to drop an accidentally committed 134MB HAR capture file\u000athat was blocking the push (GitHub's 100MB file size limit); also\u000agitignore *.har so this can't recur.\u000a\u000aCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\u000aClaude-Session: https://claude.ai/code/session_013G7aDXCyYUwGgTG81QTP8E\u000a",
        "date" : "2026-09-14 17:25:18 +0200",
        "id" : "3c67c7ed036f479cad2a91044c687bb5dbfbe6a4",
        "msg" : "Add asterisk lookup endpoint",
        "paths" : [
          {
            "editType" : "edit",
            "file" : ".env-prod"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/elasticHelper.ts"
          },
          {
            "editType" : "edit",
            "file" : "nuxt.config.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env"
          },
          {
            "editType" : "edit",
            "file" : ".gitignore"
          },
          {
            "editType" : "add",
            "file" : "server/api/telephony/cid-lookup/[number].get.ts"
          },
          {
            "editType" : "edit",
            "file" : ".env-dev"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/api/offers/conditions/index.post.ts",
          "server/utils/offers/quotePdf.ts",
          "app/components/offers/QuoteModal.vue",
          "app/composables/useOfferQuote.ts",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "0d5c36c95d22b6066ed5d0718459bb0e6e13c578",
        "timestamp" : 1789403035000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Quote: configurable grading pricing (time/fixed + description) and save-draft button\u000a\u000aGrading is now priced by time/effort or a fixed price with a freetext\u000adescription printed as a subtext, instead of a flat by-effort checkbox.\u000aAlso adds a Save button on the quote modal to persist offer_conditions\u000awithout sending the quote.\u000a",
        "date" : "2026-09-14 18:23:55 +0200",
        "id" : "0d5c36c95d22b6066ed5d0718459bb0e6e13c578",
        "msg" : "Quote: configurable grading pricing (time/fixed + description) and save-draft button",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/composables/useOfferQuote.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/offers/conditions/index.post.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/offers/quotePdf.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/offers/QuoteModal.vue"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/utils/offers/quotePdf.ts",
          "app/pages/index.vue",
          "server/utils/offers/offerConditions.ts",
          "app/components/offers/QuoteModal.vue",
          "app/composables/useOfferQuote.ts",
          "app/assets/langs/shared.js"
        ],
        "commitId" : "26efb3a1498df891c0853f9e44aae23876a7713c",
        "timestamp" : 1789403770000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Quote: customer-requirements checklist; dashboard: leads card quick actions\u000a\u000aAdd a \"Kundenanforderungen\" section to the quote modal — freetext bullet\u000apoints each answered possible/not possible (green check / red X) or with\u000aa freetext answer, printed as a checklist in the PDF.\u000a\u000aAlso add \"create lead\" and \"open all leads\" icon buttons to the dashboard's\u000alatest-leads card header.\u000a",
        "date" : "2026-09-14 18:36:10 +0200",
        "id" : "26efb3a1498df891c0853f9e44aae23876a7713c",
        "msg" : "Quote: customer-requirements checklist; dashboard: leads card quick actions",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/composables/useOfferQuote.ts"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/offers/offerConditions.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "edit",
            "file" : "app/pages/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/components/offers/QuoteModal.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/offers/quotePdf.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "app/components/settings/OrderSourceForm.vue",
          "server/api/oauth/amazon/test-connection.post.ts",
          ".env-prod"
        ],
        "commitId" : "82e20b0c80024eabe37fea705ff9d11134cfbf58",
        "timestamp" : 1789417063000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "Amazon: switch central OAuth app to Published, fix test-connection + credential UI\u000a\u000aOrder source 1000026 stopped importing because the previous central Amazon app\u000a(\"logship\", Draft) hit its mandatory 180-day LWA Client Secret rotation deadline\u000a(2026-09-12) — LWA still authenticated fine, but every SP-API call was denied.\u000aReconnecting the seller's own authorization doesn't rotate the app's secret, so\u000athat never fixed it. Standardized the central app on \"LogShip App\" (Published)\u000ainstead, matching what order source 1000000 already used successfully.\u000a\u000aAlso:\u000a- test-connection.post.ts: report failure (with Amazon's real error text) when\u000a  every SP-API endpoint is denied, instead of always defaulting to a misleading\u000a  \"missing Tax Invoicing role\" message.\u000a- OrderSourceForm.vue: hide the raw Client ID / Client Secret / Refresh Token\u000a  fields for Amazon by default (Connect/Reconnect manages them automatically);\u000a  only reveal them behind an explicit \"uses its own separate Amazon Developer\u000a  app\" toggle.\u000a",
        "date" : "2026-09-14 22:17:43 +0200",
        "id" : "82e20b0c80024eabe37fea705ff9d11134cfbf58",
        "msg" : "Amazon: switch central OAuth app to Published, fix test-connection + credential UI",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/components/settings/OrderSourceForm.vue"
          },
          {
            "editType" : "edit",
            "file" : ".env-prod"
          },
          {
            "editType" : "edit",
            "file" : "server/api/oauth/amazon/test-connection.post.ts"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "server/api/sales/sis/listings.get.ts",
          "server/api/sales/sis/order-sources.get.ts",
          "app/composables/menu/admin/sales.ts",
          "app/components/MainNavigationMenu.vue",
          "app/pages/sales/sis/index.vue",
          "server/api/sales/sis/offers.post.ts",
          "server/utils/amazonSpApi.ts",
          "server/utils/sisMatching.ts",
          "app/assets/langs/shared.js",
          "server/api/sales/sis/price-update.post.ts"
        ],
        "commitId" : "7ccda3abf09f752e54f2ad7299b242330511d169",
        "timestamp" : 1789424614000,
        "author" : {
          "absoluteUrl" : "https://jenkins.younex.de/user/info",
          "fullName" : "info"
        },
        "authorEmail" : "info@younex.de",
        "comment" : "SIS: new Amazon pricing control page — compare internal vs Amazon price/qty, Buy Box, fees, push corrected prices\u000a\u000aAdmin-only page under Sales comparing internal price/stock against what's\u000alive on Amazon for each Amazon order source, with a one-click price push\u000aback to Amazon. New Amazon SP-API surface in amazonSpApi.ts: bulk merchant\u000alistings report (Reports API, the source of truth for \"what's on Amazon\"),\u000aListings Items API read/patch, Product Pricing API (Buy Box), Fees Estimate\u000aAPI. Product matching is SKU-first (Value/SKU) with ASIN as fallback, via\u000asisMatching.ts. Verified end-to-end against live production data, including\u000aa real price push (Amazon's Listings Items PATCH turned out to be async\u000adespite returning immediately, so pushes poll briefly then trust the\u000aaccepted submission, with a short-lived overlay so a subsequent bulk report\u000arefresh can't revert a just-pushed price before Amazon's own report catches\u000aup).\u000a",
        "date" : "2026-09-15 00:23:34 +0200",
        "id" : "7ccda3abf09f752e54f2ad7299b242330511d169",
        "msg" : "SIS: new Amazon pricing control page — compare internal vs Amazon price/qty, Buy Box, fees, push corrected prices",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "app/composables/menu/admin/sales.ts"
          },
          {
            "editType" : "add",
            "file" : "app/pages/sales/sis/index.vue"
          },
          {
            "editType" : "edit",
            "file" : "app/assets/langs/shared.js"
          },
          {
            "editType" : "add",
            "file" : "server/api/sales/sis/listings.get.ts"
          },
          {
            "editType" : "edit",
            "file" : "app/components/MainNavigationMenu.vue"
          },
          {
            "editType" : "edit",
            "file" : "server/utils/amazonSpApi.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/sales/sis/offers.post.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/sales/sis/order-sources.get.ts"
          },
          {
            "editType" : "add",
            "file" : "server/utils/sisMatching.ts"
          },
          {
            "editType" : "add",
            "file" : "server/api/sales/sis/price-update.post.ts"
          }
        ]
      }
    ],
    "kind" : "git"
  },
  "culprits" : [
    {
      "absoluteUrl" : "https://jenkins.younex.de/user/info",
      "fullName" : "info"
    }
  ]
}