- MigraDocExtensions: fix a bug where capping an oversized explicit
width+height image re-derived the other dimension from the image's
native pixel ratio instead of scaling both sides uniformly, silently
distorting a deliberately non-native-ratio (e.g. stretched) image.
- InvoiceService: derive the mfr-portal image whitelist prefix from the
existing Fds:MFR_host config setting (via MFRClientConfig.RestRoot)
instead of a second hardcoded literal, so it can't drift from the
actual configured host; parse <img> sources with HtmlAgilityPack
(already used by the PDF's own HTML converter) instead of a regex, so
a decoy attribute like data-src can't be picked up in place of the
real src; dedup fetch URLs with a HashSet instead of O(n^2) List
scanning; reuse FuchsPdf.MigraDocFilenameFromByteArray instead of
re-spelling the "base64:" prefix.
- Fix a stale log message and comment left over from the
GenerateInvoicePdf -> GenerateInvoicePdfAsync rename, and update
INVOICE_LIFECYCLE.md's references to the old sync method name.
- Add tests for the config-derived whitelist, a decoy-attribute src,
and the 20-image-per-invoice fetch cap boundary.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Fuchs/gulpfile.js: pass encoding:false to gulp.src() in the copy task —
vinyl-fs otherwise UTF-8-decodes binary files by default, which was
silently corrupting the glyphicons webfont (and any other binary asset
copyconfig.json matches) on every `gulp copy`/`gulp all` run.
- Invoice/reminder editor: replace the row's flat action icons (edit/
delete/insert/set-price) with a single context button opening a
dropdown menu, reusing the existing $ocms.menu toolbar mechanism.
- Fix "-1" leaking into the Pos. column for Text/Title rows: $inv.rrw's
`dta.position || dta.SortOrder` fallback treated the numbering's
intentional empty string as unset and fell back to SortOrder (-1 for
such rows); now only a genuinely uncomputed position falls back.
- Zusatzempfänger (CC) is now edited together with the main invoice
email in one combined dialog ($inv.emailFrm/$inv.eEmail), wired into
all three entry points (create, continue, storno) instead of a
separate hover-revealed row with its own edit target.
- Add tooltips to every row/header edit control, including several that
had none before (header field pencils, the request/section title
edit button), phrased as object+verb ($rct.frmTT).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The invoice line editor already shows <img> pictures pulled from the mfr
portal, but the PDF pipeline's HTML-to-MigraDoc converter had no <img>
handler, so they never appeared on the actual invoice.
InvoiceService now fetches only https://portal.mobilefieldreport.com/mfr/*
image sources before rendering (size/type/count capped, failures logged and
skipped) and hands the resolved images down through ApplyInvoice into the
HTML converter, which renders them inline at their original HTML position
and size (px converted to cm, capped against layout overflow). See ADR 0018.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous gulp-copy refresh (3111347) mangled the binary
eot/ttf/woff/woff2 files through a text-mode path, replacing invalid
byte sequences with U+FFFD and desyncing each file's internal length
header from its actual size — hence the browser's "Failed to decode
downloaded font" error in production. Reverted the four files to the
last known-good (pre-corruption) bytes and marked font extensions as
binary in .gitattributes so text=auto can never normalize them again.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Picks up the currently installed glyphicons font from node_modules,
which now includes the arrow-up/arrow-down/triangle-right/
triangle-bottom glyphs used by the set-structure dialog's reorder and
fold controls (ADR 0016) — the previously copied font predates those
icons. Also adds an empty scratch file the user had open in
Fuchs/tmp/.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the InvoiceLineNumbering class that InvoiceDraftCalculator.
RecomputePositions, InvoiceSetPricing.AssignPositionLabels and their
callers already depend on (this dependency was pushed in ecc74cf
without the class itself, breaking the build) — position labels are
now hierarchical ("1", "1.1", "1.2", …) matching a line's ADR 0016
nesting level, computed by one pure, shared algorithm for both the
online editor and the PDF so they can never disagree (ADR 0008).
Also fixes an InvoiceDraftServiceTests assertion that still expected
the old flat integer position field; it is a string label now.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets an invoice carry an additional Cc recipient alongside the primary
SendToEmail, sent via the ProcessWeb mailer API. Honors the
Fuchs:Email:OverrideRecipient dev safety net: when the override is
active, Cc is dropped entirely (only the single override inbox is
ever reached), never leaked to it.
- IComService.SendEmailAsync / ProcessWebComService: new optional
`cc` parameter, included in the mailer payload only when non-empty.
- FdsInvoiceData.RawInvoiceEmailCc + BuildInvoiceParams: reads
`new.invoiceemailcc`, posts @SendToEmailCc.
- IntranetController.Requests: reads SendToEmailCc from the loaded
invoice row and passes it through on send/resend.
- Fuchs_Database: SendToEmailCc column on fds__invoices, threaded
through fds__createInvoice/fds__setInvoice/fds__getInvoice and the
fds__tt__invoice_base/_core table types.
- fis.req_txt_de.js: "Zusatzempfänger (CC)" field label.
- Tests: ProcessWebComServiceTests (Cc included/omitted/dropped under
override); SystemStatusServiceTests mocks updated for the new
IComService.SendEmailAsync parameter.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets a back-office user correct/define which invoice line belongs to
which "set" and how deep it is nested, instead of relying only on the
implicit "everything after a Set header until the next Set" grouping
fds__prepInvoice derives. Sets may contain sets; every change applies
immediately to the cached draft session and is reflected in the editor
and PDF (ADR 0006/0008).
Backend:
- InvoiceSetStructure: pure parent-link forest (assign, change level,
create, place/move-sibling, depth-first linearise + level recompute).
- InvoiceDraftSetStructureDeltas: new inv/dpatch targets set.assign,
set.level, set.create, set.move.
- InvoiceSetPricing.Build is now order-preserving and nesting-aware
(governed by the nearest converted ancestor); the set-item switch
sums a header's transitive descendants so nested conversions never
double-count.
- FuchsPdf indents nested set members per level, mirroring the editor.
Frontend:
- New fis.inv_sets.js: "Set-Struktur bearbeiten" dialog — per-block
table of all lines with drag & drop reorder (before/after/into),
▲▼ order and ◀▶ level buttons, fold/unfold per set (view-only), and
a create bar for new Set/Text lines. Posts one delta per action; the
dialog is a pure view of the server state (ADR 0006).
- _Layout.cshtml / fis_main.js: cache-bust the dynamically imported
per-module bundles (web/fis.<mdl>.<locale>.js/.css) with an asset
version derived from the newest bundle write time, so a gulp rebuild
is picked up without a stale cached module.
Docs: ADR 0016, live-draft-editing concept, INVOICE_SET_PRICING.md,
USER_GUIDE.md, ARCHITECTURE.md updated. Tests: InvoiceSetStructureTests
(new) + additions to InvoiceDraftServiceTests/InvoiceSetPricingTests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add datetime_LocalToUTC / datetime_utcToLocal, and two widget query
functions (fds_wdg_MFRSync, fds_wgd_lastEmails). Remove the unreferenced
backup__fds__* function files superseded by their current counterparts,
and suppress SQL71501 (unresolved cross-database/composite references)
project-wide.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Splits fn=admin into two independent modules: the original status
dashboard stays at fn=admin, user management moves to a new fn=usr
(IntranetController.AdminUsers.cs, fis.usr.js). Both are now reached
through a single "Administration" sidemenu dropdown (Systemüberwachung /
Benutzerverwaltung) gated by one shared fds_sys > 4 check, per ADR 0015
(supersedes 0014's "fold into one module" choice).
Also fixes the password-reset validation to check only a plain 6-char
minimum instead of silently also requiring ValidatePassword's default
2-uppercase/2-lowercase/2-digit composition, which contradicted the
"mind. 6 Zeichen" message shown to the admin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OCORE, OCORE_Charting, OCORE_web, eRechnungLib each moved forward with
their own NuGet dependency updates.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remove fds__getBankingtransaction_manual.sql, superseded by the
already-referenced fds__getBankingtransaction_manual_1.sql, and four
unreferenced loose copies sitting outside the SSDT folder structure at
the project root (fds__getInvoiceFileContent.sql,
fds__getInvoiceFiles_ForBlobArchive.sql, fds__getReminderFileContent.sql,
fds__getReminderFiles_ForBlobArchive.sql) - the real, referenced
versions live under dbo/Stored Procedures/. Also wires the
already-committed fds__getCompanyAddressJson.sql function into the
project's build list.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Routine dependency bumps (OpenTelemetry, Identity/JWT, ImageSharp,
HtmlAgilityPack, BouncyCastle.Cryptography, Microsoft.Extensions.*,
xunit.v3/Test SDK) across Fuchs, Fuchs.Tests, Fuchs_DataService, and
MFR_RESTClient. Also rebuilds the tools.js bundle and adds the VB.NET
Companion language-bridge setting used for the Intranet_Legacy VB code.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
6.8.6 is the last MIT-licensed release; 7.0+ requires a commercial
license. Documents the constraint and syncs it across CLAUDE.md,
CODEX.md, and copilot-instructions.md per the instruction-sync rule.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wires the previously-unused fis_admin_* user/group stored procs into the Admin
module (fds_sys-gated), adds a users grid with a per-row ribbon (reset
password, change email, manage group membership), and always emails a
password reset result to the user rather than showing it to the admin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sets submodule.<name>.branch = main so `git submodule update --remote`
resolves each submodule to the tip of main without needing a
per-invocation --branch argument.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fast-forward all five submodules (OCORE, OCORE_web, OCORE_web_pdf,
OCORE_Charting, eRechnungLib) to the tip of their respective main
branches.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Completes the eRechnung output path: finalized invoices are emitted as a
ZUGFeRD/Factur-X EN 16931 hybrid (DATEV) or, when a Leitweg-ID is present, as
XRechnung 3.0 for B2G. Both are externally validated as ACCEPTED (0 errors) and
PDF/A-3B COMPLIANT against the ProcessWeb eInvoice service.
- ERechnungMapper: FdsInvoiceData -> EN 16931 model. Seller master data now from
Fuchs:ERechnung:Seller config (VAT id BT-31, Steuernummer BT-32, optional
Handelsregister BT-30, IBAN/BIC, contact). Adds payment terms BT-20/BT-9
(BR-CO-25), buyer VAT id, §13b reverse charge, and the service date/period
(BT-72 / BG-14) parsed from ProvisionPeriod (BR-DE-TMP-32). B2G -> XRechnung
profile with the required electronic addresses/contact.
- ERechnungValidator: client for POST /validatepdf (EN 16931 XML + PDF/A-3 in
one call). A pure "scenario not matched" with zero errors is not treated as a
hard failure; real errors optionally withhold the hybrid (FailOnError).
- ERechnungSettings: seller + validation config; wired in Program.cs; ServiceUrl
in appsettings.
- Online editor: structured German-only dialogs for the recipient address
(incl. Leitweg-ID) and the service date/period (single date or range).
- Bumps the eRechnungLib submodule to the CII rsm-namespace / PDF-A subtype fix.
Fuchs.Tests 514/514, eRechnungLib 97/97.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Created a new function `fds__getCompanyAddressJson` to return a company's postal address as a structured JSON object.
- Modified stored procedures `fds__createInvoice`, `fds__setInvoice`, and `fds__prepInvoice` to include a new parameter `@SendToAddressJson` for handling the address data.
- Updated the invoice table and user-defined types to accommodate the new `SendToAddressJson` field.
- Ensured that the address data is properly retrieved and stored in the invoice records.
- Modified the stored procedure `fds__admin_getReportCatalog.sql` to use the correct schema for `all_objects`.
- Added new folders and projects for `eRechnungLib` in the solution file `Fuchs_Intranet.slnx`, including validation and test projects.
- Updated submodule reference for `OCORE`.
- Added new submodule `eRechnungLib` with initial commit.
- FdsConfig.Initialize now accepts IConfiguration for DI support; Program.cs updated to pass builder.Configuration.
- Gulp "copy" task logs copied files for better feedback.
- Bank statement file input now accepts all file types.
- Updated glyphicon font binary assets.
Front-end (fis.inv_shared.js / fis.inv_txt_de.js, rebuilt bundles):
- 3-way set display switch (setprice/itemprices/setonly) via admin.setmode,
emitted into InvoiceOptions by FdsInvoiceData.BuildInvoiceOptions.
- Each request block now posts items[] in the backend contract shape
(title/desc/qty/price_net/total_net + set type/setId tags) via itemToContract.
- invcPayload normalises the editor model to the field names BuildInvoiceParams
reads (sms totals -> new.total_net/total_gross, invoicetitle->title,
loc->provisionlocation, admin.paymentterms->new.paymentterm, CustomerId->customerid).
Back-end:
- BuildInvoiceOptions adds the setmode token alongside §13b.
- VAT rate+amount now taken from sms.vat (HighestVat) instead of the broken
items 'is List<object>' detection that pinned the rate to 19.
- InvoiceSetPricing blanks price/total cells for text/title heading lines.
Tests: set-pricing text-line blanking, HighestVat selection/parsing, updated
the VAT param test to the sms.vat contract. 180 passing.
Note: the second VAT slot (InvoiceVAT_2) stays unused by design; mixed-rate
invoices store only the highest rate (pre-existing, accepted).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assesses the proposed server-held edit state + SignalR live-edit channel against
the current stateless design. Recommendation: don't do the full rewrite now (it
adds stateful-server/scaling/reconnect complexity for a single-editor back-office
flow); instead add a stateless inv/calc endpoint that reuses the backend pricing
(InvoiceSetPricing/VAT) so the editor stops duplicating the math — capturing the
real value without sockets. Add SignalR later only as transport if real-time
co-editing becomes a genuine requirement.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sets (mfr__items Type='set') can be shown three ways on the invoice:
- SetPrice (default): set line priced, member items shown without price
- ItemPrices: member items priced, set line as a heading without price
- SetOnly: only the set line (priced), members removed
- InvoiceSetPricing (new): the authoritative, unit-tested transformation
(SetDisplayMode + Build) that both sides agree on; set price always equals the
sum of members. Mode is read from InvoiceOptions ("setmode:<mode>").
- FuchsPdf.ApplyInvoice renders through it: lines flagged ShowPrice=false print
blank price/total cells; set headers are emphasised. Invoices without sets are
unchanged. Totals come from the registration balance, so modes are purely
presentational and never change the sum.
- InvoiceSetPricingTests (+14): all three modes, set-price = member sum, header
total fallback, no-set pass-through, option parsing.
- Docs/INVOICE_SET_PRICING.md documents the front-end contract (the editor sets
the mode token + tags set header/member items); the back-end does the rest.
Front-end editor wiring is specified in the doc but intentionally not shipped
blind (cannot validate the running editor here).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reviewed the data service against mfr_interface_description.md. The OData entity
sync already follows @odata.nextLink and now inherits the MFR client's transient
retry + timeout, so it is spec-aligned. Reliability/performance improvements:
- MFRClient.GetFile no longer news up an HttpClient per call (socket-exhaustion
risk); added GetFileAsync backed by one shared static HttpClient with
per-request auth, and GetFile delegates to it.
- GetInvoiceFiles_async now downloads + stores invoice PDFs in parallel
(bounded concurrency 4) via Parallel.ForEachAsync instead of sequentially.
- Threaded CancellationToken from the MfrSync job through UpdateIfNecessary_async/
UpdateRequested_async/GetInvoiceFiles_async and the entity-sync loops for
graceful shutdown (cooperative checks between iterations). Entity-table sync
is left sequential on purpose (referential ordering by updateneed).
IFdsMfr sync methods gained optional CancellationToken params (default) — the
web app only uses the read methods, so this stays source-compatible with Fuchs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ARCHITECTURE.md: add Fuchs_Database (SSDT) and the MFR interface doc to the
project table; new MFR ERP Integration and Database sections.
- copilot-instructions.md + CLAUDE.md (kept in sync): add MFR ERP integration
and Database sections pointing to MFR_RESTClient/Docs/mfr_interface_description.md
as the contract to read before changing the client; CLAUDE.md doc map updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified every [dbo].[...] object the backend calls against the SSDT project.
Two real mismatches fixed (both would fail at runtime):
- Banking search (bam/btl mode=s) called a non-existent
[dbo].[fds__getBankingtransactions_list2] and dropped @tgtdate. The actual
proc (and the legacy call) is [dbo].[fds__getBankingtransfers_list2]
(@tgtdate,@mode,@search,@authuser) — corrected name + parameters.
- Widget generic branch called a phantom [dbo].[fds__getWidget] that never
existed (legacy only had my/one; the dashboard only requests wdg/my, wdg/one).
The default branch now returns 404 instead of hitting a missing proc.
(The 'fuchs__admin_logdebug' reference is only in a commented-out line.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the SQL Server Data Tools project for the fuchs_fds database — tables,
table types, functions and stored procedures that the backend calls (e.g.
fds__getInvoice, fds__merge_bankingtransactions, fds__tt__bankingtransactions,
fds__admin_getReportCatalog, fis_* auth). Build/model caches (bin, obj,
*.dbmdl, *.jfm, *.user) are git-ignored.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ARCHITECTURE.md: reflect the implemented DI service layer, CAMTParser,
OpenTelemetry/observability, the ported report engine, and CAMT+MT940
banking; mark the resolved observations.
- copilot-instructions.md: add Services/DI, dual-format banking, observability
and testing sections; add an Instruction-Sync banner.
- CLAUDE.md (new): Claude Code project instructions mirroring the shared rules,
plus build/test workflow notes. Both files state they must stay in sync.
- USER_GUIDE.md (new, Fuchs/Docs): end-user process guide (login, invoices,
reminders, requests, banking incl. MT940/CAMT upload, DATEV, reports).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New CAMTParser project: namespace-agnostic parser for camt.052/053/054
producing a statement/entry model aligned with the banking columns
(account, amount, debit/credit, dates, counterparty, references, remittance).
- BankingService now auto-detects the upload format (XML→CAMT, else MT940)
and maps either into the same fds__tt__bankingtransactions DataTable, so the
bam/up handler transparently accepts both.
- Frontend (fis.bam.de.js) upload field now advertises accept for both
MT940 (.sta/.mt940/.txt) and CAMT (.xml/.camt).
- Tests (+14, 151 total): CamtParserTests cover parsing (credit/debit,
namespace-version agnostic, reversals), detection, and failure/edge inputs
(empty, invalid XML, non-CAMT); BankingDualFormatTests verify CAMT and MT940
both land in the same DataTable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the intranet off the static-helper / Active-Record pattern onto
constructor-injected services, removing controller coupling and the
sync-over-async (Task.Run().Wait()) hot spots in the data classes.
Services now registered and consumed via DI:
- IBankingService, IPdfService, IMfrClientFactory (singletons)
- IWidgetService, IReportService, IInvoiceService, IReminderService (scoped)
Key changes:
- FuchsWidgetService: real widget logic (sql_table/indicator/html +
rendering_options) ported from the static class, which is deleted.
- FuchsReportService + FuchsVisualization: report engine decoupled from
IntranetController (takes connStr/dbSec/userAccountId); static
FuchsReports deleted.
- InvoiceService / ReminderService: implement load/register/render/store
(previously NotImplementedException stubs). FdsInvoiceData /
FdsReminderData are now pure data holders — all DB + PDF work moved into
the services, async throughout (no Task.Run().Wait()).
- Controllers inject and call the services; all `new FdsMfrClient()` calls
go through IMfrClientFactory.
- Deleted dead code: static Banking, FuchsWidgets, FuchsReports, and the
unused IDbConnectionFactory.
- InternalsVisibleTo("Fuchs.Tests") for testing internal mapping logic.
Tests: 127 passing (Banking tests moved to the service; added data-holder
tests for FdsInvoiceData/FdsReminderData). Full solution builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Close functional regressions found by comparing the legacy applications
(Intranet_Legacy/) against their C# counterparts:
- ProcessWebComService: send attachments inline (base64) with the
push_com POST so invoice/reminder PDFs are attached again.
- FuchsPdf: wire GetPaycode into ApplyInvoice/ApplyReminder to restore
the SEPA giro-code payment QR, and restore the full standard invoice
text block (§35a labor-cost note, Akonto text, §14/§48 notes, AGB,
Steuernummer, Verrechnungssätze, etc.).
- IntranetController: restore changepassword validation (password
strength, confirmation match, current-password verification) and the
mfr empty-id OData $metadata response.
- Reports: port the ocms_visualization engine to C# (FuchsVisualization)
and wire FuchsReports.ProcessFdsRequest to render generic/
generic_content/chart reports instead of returning an empty OK stub.
Adds smoke tests for the giro QR generator and report page builder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extensive structured logging was added throughout IntranetController and all invoice/account handlers to improve traceability and debugging. Logging now covers action entry/exit, error conditions, and key parameters (user IDs, invoice IDs, etc.). Handlers log warnings for missing/invalid input and info/debug for significant events. Minor refactoring extracts form values for better logging. The jQuery `rwText` plugin was hardened against null input. Updated minified JS, font assets, and OCORE submodule. No functional changes to `tools.js`.
Bump Microsoft.IdentityModel.* packages to 8.19.1 in MFR_RESTClient and OCORE. Update Microsoft.NET.Test.Sdk and coverlet.collector in Fuchs.Tests. Refresh OCORE submodule reference. No functional changes; dependency updates only.
Integrate OCORE/OCORE_web-based secret management using Azure Key Vault and DPAPI cache. Update appsettings.json to remove plaintext secrets and list managed keys. Register secret management in Program.cs. Update .gitignore for secret files. Add documentation for naming conventions and migration, plus a PowerShell script for initial secret upload. Centralizes and secures secret handling across the app.