1376779224
- 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>
428 lines
24 KiB
Markdown
428 lines
24 KiB
Markdown
# Fuchs Intranet — Solution Architecture
|
|
|
|
> Auto-generated architecture analysis
|
|
> .NET 10 · ASP.NET Core MVC · SQL Server
|
|
|
|
---
|
|
|
|
## 1. Solution Overview
|
|
|
|
The **Fuchs Intranet** solution is a line-of-business web application for **Sebastian Fuchs Bad und Heizung GmbH & Co. KG** (a plumbing/heating company in Düsseldorf). It manages invoices, reminders, service requests, banking transactions, reports, and user authentication — all exposed through a single-page intranet front-end.
|
|
|
|
| Project | Type | Purpose |
|
|
|---|---|---|
|
|
| **Fuchs** | ASP.NET Core Web (MVC) | Main web application — the intranet |
|
|
| **Fuchs_DataService** | Console / Windows Service (Topshelf) | Background data sync service (MFR ERP polling) |
|
|
| **MFR_RESTClient** | Class Library | REST/OData client for the MFR ERP system |
|
|
| **OCORE** | Class Library (shared) | Core utilities: SQL, crypto, email, IO, logging |
|
|
| **OCORE_web** | Class Library (shared) | Web utilities: MVC helpers, middleware, auth, captcha |
|
|
| **OCORE_web_pdf** | Class Library (shared) | PDF generation (MigraDoc/PDFsharp, HTML→PDF) |
|
|
| **OCORE_Charting** | Class Library (shared) | Data visualization / charting (ported System.Windows.Forms.DataVisualization) |
|
|
| **MT940Parser** | Class Library (external) | SWIFT MT940/MT942 bank statement parser |
|
|
| **CAMTParser** | Class Library (in-repo) | ISO 20022 CAMT (camt.052/053/054) bank statement parser |
|
|
|
|
**All projects target `net10.0`.**
|
|
|
|
---
|
|
|
|
## 2. Architecture Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ CLIENTS (Browser) │
|
|
│ SPA-like JS front-end (js/intranet/) │
|
|
└──────────────────────────────┬──────────────────────────────────────────┘
|
|
│ HTTP (GET / POST)
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Fuchs (ASP.NET Core MVC) │
|
|
│ │
|
|
│ Program.cs ─── ConfigureServices / ConfigureApp │
|
|
│ │ │
|
|
│ ├── Cookie Authentication (scheme: "fuchs_intranet") │
|
|
│ ├── Distributed Memory Cache │
|
|
│ └── DI Registrations: │
|
|
│ • Fuchs_intranet (singleton — config + auth + DB helper) │
|
|
│ • IFdsMfr → FdsMfr (singleton — ERP sync) │
|
|
│ │
|
|
│ Routes: │
|
|
│ /{fn?}/{id?}/{code?} → IntranetController.Index (SPA shell) │
|
|
│ /do/{fn?}/{id?}/{code?} → IntranetController.Do (API) │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|
│ │ IntranetController (partial class) │ │
|
|
│ │ │ │
|
|
│ │ .cs — core: Index, Do dispatcher, Auth, Login/Logout │ │
|
|
│ │ .Invoices.cs — invoice CRUD, DATEV export, PDF │ │
|
|
│ │ .Invoices2.cs— invoice sub-handlers (MFR refresh, get, list) │ │
|
|
│ │ .Reminder.cs — payment reminder CRUD + PDF │ │
|
|
│ │ .Requests.cs — service request management │ │
|
|
│ │ .Banking.cs — MT940 upload, transaction queries │ │
|
|
│ │ .Reports.cs — report catalog, execution │ │
|
|
│ └──────────┬──────────────────────────────────────────────────────┘ │
|
|
│ │ calls │
|
|
│ ┌──────────▼──────────────────────────────────────────────────────┐ │
|
|
│ │ code/ (Business Logic) │ │
|
|
│ │ │ │
|
|
│ │ FuchsIntranet.cs — Fuchs_intranet singleton (config, auth, │ │
|
|
│ │ DB connections, debug logging) │ │
|
|
│ │ FdsInvoiceData.cs — Invoice data model + PDF generation │ │
|
|
│ │ FdsReminderData.cs — Reminder data model + PDF generation │ │
|
|
│ │ FuchsPdf.cs — PDF layout/rendering (MigraDoc) │ │
|
|
│ │ FuchsWidgets.cs — Dashboard widget data (SQL-driven) │ │
|
|
│ │ FuchsReports.cs — Report dispatch │ │
|
|
│ │ FuchsFdsEmail.cs — Email sending + DB logging │ │
|
|
│ │ Banking.cs — MT940 parsing to DataTable │ │
|
|
│ │ MigraDocExtensions — MigraDoc helper extensions │ │
|
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ Logging/FuchsLoggerProvider.cs — custom ILoggerProvider │
|
|
└────────┬────────────────┬──────────────────┬───────────────────────┬────┘
|
|
│ │ │ │
|
|
▼ ▼ ▼ ▼
|
|
┌──────────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────────────┐
|
|
│ OCORE │ │ OCORE_web │ │ OCORE_web_pdf │ │ MT940Parser │
|
|
│ │ │ │ │ │ │ │
|
|
│ • SQL helper │ │ • MVC helper │ │ • MigraDoc/ │ │ • SWIFT MT940/ │
|
|
│ (ADO.NET) │ │ (JSON,File)│ │ PDFsharp │ │ MT942 parser │
|
|
│ • Email │ │ • Cookie Auth│ │ • HTML→PDF │ │ │
|
|
│ • IO/Files │ │ • Middleware │ │ • Font resolver │ │ │
|
|
│ • Crypto │ │ • Captcha │ │ │ │ │
|
|
│ • Logging │ │ • Background │ │ │ │ │
|
|
│ • CSV/XML │ │ services │ │ │ │ │
|
|
│ • DateTime │ │ • Security │ │ │ │ │
|
|
└──────────────┘ └──────────────┘ └────────────────┘ └──────────────────┘
|
|
│ │
|
|
▼ ▼
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Fuchs_DataService (Windows Service / Console) │
|
|
│ │
|
|
│ FdsMain.cs — Topshelf host, job definitions │
|
|
│ PeriodicHostedService — BackgroundService with PeriodicTimer │
|
|
│ FdsMfr.cs (IFdsMfr) — MFR sync orchestration │
|
|
│ FdsMfrClient.cs — MFR REST client wrapper │
|
|
│ FdsShared.cs — FdsConfig (appsettings.json reader) │
|
|
│ FdsZip.cs — 7-Zip archive handling (DATEV export) │
|
|
│ FdsDebug.cs — Debug/file logging │
|
|
│ │
|
|
│ Jobs: MfrSync (every N min) │
|
|
│ → UpdateIfNecessary_async (entity table sync) │
|
|
│ → UpdateRequested_async (on-demand entity refresh) │
|
|
│ → GetInvoiceFiles_async (invoice PDF download) │
|
|
└────────────────────────┬────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ MFR_RESTClient │
|
|
│ │
|
|
│ MFRClient.cs — RestSharp-based REST/OData client │
|
|
│ MFRClientModels.cs — Config, credentials, entity types │
|
|
│ ODataEnvelope.cs — OData response wrapper │
|
|
│ Entities/MfrGeneric.cs— Generic entity helpers │
|
|
└────────────────────────┬────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────┐
|
|
│ MFR ERP System │
|
|
│ (External REST) │
|
|
└─────────────────────┘
|
|
|
|
┌─────────────────────┐
|
|
All projects ──────►│ SQL Server │
|
|
│ (fuchs_fds DB) │
|
|
│ Stored procedures │
|
|
│ Symmetric key enc. │
|
|
└─────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Dependency Graph
|
|
|
|
```
|
|
Fuchs (Web)
|
|
├── OCORE
|
|
├── OCORE_web ──► OCORE
|
|
├── OCORE_web_pdf
|
|
├── MT940Parser
|
|
├── MFR_RESTClient
|
|
└── Fuchs_DataService
|
|
├── OCORE
|
|
├── OCORE_web
|
|
└── MFR_RESTClient
|
|
|
|
OCORE_Charting (standalone — referenced by solution but no direct project reference)
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Key Architectural Patterns
|
|
|
|
### 4.1 Partial Controller Pattern
|
|
`IntranetController` is split across **7 partial-class files**, each handling a business domain (invoices, reminders, banking, etc.). The main `Do()` action uses a `switch` expression to dispatch to domain-specific methods.
|
|
|
|
### 4.2 Singleton Configuration Object
|
|
`Fuchs_intranet` is a manually-managed singleton (via `FuchsOcmsIntranet`) initialized at startup with `IConfiguration`. It holds connection strings, app settings, auth helpers, and DB connection factory methods.
|
|
|
|
### 4.3 Service Layer (Dependency Injection)
|
|
Business logic lives in **DI-registered services** under `Fuchs/Services/` behind interfaces, injected into `IntranetController`:
|
|
`IComService`, `IPdfService`, `IInvoiceService`, `IReminderService`, `IReportService`, `IWidgetService`, `IBankingService`, `IMfrClientFactory`.
|
|
Stateless services (`IPdfService`, `IBankingService`, `IMfrClientFactory`) are singletons; DB/request-scoped services are scoped (see `Program.cs`).
|
|
`FdsInvoiceData` / `FdsReminderData` are now **pure data holders** (parse + properties); loading, persistence and PDF generation live in the services (fully async — no `Task.Run(...).Wait()`).
|
|
`FuchsPdf` / `FuchsVisualization` remain as static rendering libraries used *by* the services. The earlier static, controller-coupled helpers (`FuchsWidgets`, `FuchsReports`, `Banking`, `FuchsFdsEmail`) have been removed.
|
|
|
|
### 4.4 SQL-First Data Access
|
|
There is no ORM (no EF Core). All data access uses **ADO.NET via OCORE SQL helpers** (`getSQLDatatable_async`, `getSQLDataSet_async`, `setSQLValue_async`) calling stored procedures and inline SQL. `DataTable`/`DataRow` is the primary data transfer mechanism.
|
|
|
|
### 4.5 Background Service
|
|
`Fuchs_DataService` runs as a Windows Service (Topshelf) with a `PeriodicHostedService` that polls the MFR ERP system on a timer, syncing entities and downloading invoice files.
|
|
|
|
### 4.6 Authentication
|
|
Cookie-based authentication (`CookieAuthenticationDefaults`) with custom claims (`FuchsUserIdentity`). SQL-based user/password verification.
|
|
|
|
---
|
|
|
|
## 5. Service Layer (implemented)
|
|
|
|
> **Status: DONE.** The services below are implemented and DI-registered in
|
|
> `Program.cs`. The original extraction rationale is retained for reference /
|
|
> history. `FuchsFdsEmail` → `IComService` (ProcessWeb Mailer API, inline
|
|
> base64 attachments), `FuchsWidgets` → `IWidgetService`, `FuchsPdf` →
|
|
> `IPdfService`, `Banking` → `IBankingService` (now MT940 **and** CAMT),
|
|
> `FdsInvoiceData`/`FdsReminderData` → `IInvoiceService`/`IReminderService`
|
|
> (data classes are now pure POCOs), `FuchsReports` → `IReportService`
|
|
> (backed by the ported `FuchsVisualization` engine), `FdsMfrClient` →
|
|
> `IMfrClientFactory`.
|
|
|
|
---
|
|
|
|
### 5.1 `FuchsFdsEmail` → `IEmailService`
|
|
|
|
**Current state:** Static class using `System.Configuration.ConfigurationManager` (legacy!) and receiving `Fuchs_intranet` as a parameter.
|
|
|
|
**Problem:** Uses `cfg.AppSettings["FDS_EmailSettings"]` — violates the project's own rule to not use `System.Configuration.ConfigurationManager`. Untestable. Static state (`_settings` cache).
|
|
|
|
**Proposed service:**
|
|
```csharp
|
|
public interface IEmailService
|
|
{
|
|
Task<bool> SendEmailAsync(string reference, string subject, string html,
|
|
string email, string name, Dictionary<string, byte[]>? attachments);
|
|
}
|
|
|
|
public class FuchsEmailService : IEmailService
|
|
{
|
|
private readonly EmailServerSettings _settings;
|
|
private readonly Fuchs_intranet _intranet;
|
|
// Inject IConfiguration, Fuchs_intranet, ILogger<FuchsEmailService>
|
|
}
|
|
```
|
|
**Registration:** `builder.Services.AddSingleton<IEmailService, FuchsEmailService>();`
|
|
|
|
**Files affected:** `Fuchs\code\FuchsFdsEmail.cs`, all callers in controller partials.
|
|
|
|
---
|
|
|
|
### 5.2 `FuchsPdf` → `IPdfService`
|
|
|
|
**Current state:** Large static class with static helper methods, hardcoded license key, company-specific constants.
|
|
|
|
**Problem:** Not injectable, not testable, mixes configuration (colors, company data) with PDF rendering logic.
|
|
|
|
**Proposed service:**
|
|
```csharp
|
|
public interface IPdfService
|
|
{
|
|
Document CreateInvoicePdf(FdsInvoiceData invoice);
|
|
Document CreateReminderPdf(FdsReminderData reminder);
|
|
Task<PdfImageCollection> DocToImageCollectionAsync(Document doc);
|
|
}
|
|
|
|
public class FuchsPdfService : IPdfService
|
|
{
|
|
// Inject ILogger<FuchsPdfService>
|
|
// Company data could come from IOptions<FuchsPdfOptions>
|
|
}
|
|
```
|
|
**Registration:** `builder.Services.AddSingleton<IPdfService, FuchsPdfService>();`
|
|
|
|
**Files affected:** `Fuchs\code\FuchsPdf.cs`, `FdsInvoiceData.cs`, `FdsReminderData.cs`, controller partials.
|
|
|
|
---
|
|
|
|
### 5.3 `FuchsWidgets` → `IWidgetService`
|
|
|
|
**Current state:** Static class that receives the entire `IntranetController` as a parameter to access `_intranet`, `UserAccountID`, `DbSec`, etc.
|
|
|
|
**Problem:** Tight coupling to controller — passes the whole controller instance. Cannot be unit tested independently.
|
|
|
|
**Proposed service:**
|
|
```csharp
|
|
public interface IWidgetService
|
|
{
|
|
Task<object> GetUserWidgetsAsync(string userAccountId, DatabaseSecurity dbSec);
|
|
Task<object> GetWidgetDataAsync(string widgetId, string userAccountId, DatabaseSecurity dbSec);
|
|
Task<object> GetSingleWidgetAsync(string shortName, string userAccountId, DatabaseSecurity dbSec);
|
|
}
|
|
|
|
public class FuchsWidgetService : IWidgetService
|
|
{
|
|
private readonly Fuchs_intranet _intranet;
|
|
// Inject Fuchs_intranet, ILogger<FuchsWidgetService>
|
|
}
|
|
```
|
|
**Registration:** `builder.Services.AddScoped<IWidgetService, FuchsWidgetService>();`
|
|
|
|
**Files affected:** `Fuchs\code\FuchsWidgets.cs`, `IntranetController.cs` (Do method).
|
|
|
|
---
|
|
|
|
### 5.4 `Banking` → `IBankingService`
|
|
|
|
**Current state:** Static class with MT940 parsing logic.
|
|
|
|
**Problem:** Minor — already fairly stateless, but takes `ILogger` as parameter instead of injection.
|
|
|
|
**Proposed service:**
|
|
```csharp
|
|
public interface IBankingService
|
|
{
|
|
DataTable ParseMT940(Stream stream, DataTable? schema = null);
|
|
}
|
|
|
|
public class BankingService : IBankingService
|
|
{
|
|
private readonly ILogger<BankingService> _logger;
|
|
// Inject ILogger
|
|
}
|
|
```
|
|
**Registration:** `builder.Services.AddSingleton<IBankingService, BankingService>();`
|
|
|
|
**Files affected:** `Fuchs\code\Banking.cs`, `IntranetController.Banking.cs`.
|
|
|
|
---
|
|
|
|
### 5.5 `FdsInvoiceData` / `FdsReminderData` → Factory Services
|
|
|
|
**Current state:** Data model classes that directly call stored procedures and PDF generation in their constructors/methods. They receive `IntranetController` as a parameter for DB access.
|
|
|
|
**Problem:** Business objects doing their own persistence (Active Record anti-pattern). Tightly coupled to controller.
|
|
|
|
**Proposed services:**
|
|
```csharp
|
|
public interface IInvoiceService
|
|
{
|
|
Task<FdsInvoiceData> LoadInvoiceAsync(string id, string userAccountId);
|
|
Task<string> RegisterInvoiceAsync(FdsInvoiceData invoice, bool change);
|
|
Task<Document> GenerateInvoicePdfAsync(FdsInvoiceData invoice);
|
|
}
|
|
|
|
public interface IReminderService
|
|
{
|
|
Task<FdsReminderData> LoadReminderAsync(string id, string userAccountId);
|
|
Task<string> RegisterReminderAsync(FdsReminderData reminder, bool change);
|
|
Task<Document> GenerateReminderPdfAsync(FdsReminderData reminder);
|
|
}
|
|
```
|
|
**Registration:** `builder.Services.AddScoped<IInvoiceService, InvoiceService>();`
|
|
|
|
**Files affected:** `FdsInvoiceData.cs`, `FdsReminderData.cs`, all controller partials that create these objects.
|
|
|
|
---
|
|
|
|
### 5.6 `FuchsReports` → `IReportService`
|
|
|
|
**Current state:** Static class with a single dispatch method, receives controller.
|
|
|
|
**Proposed service:**
|
|
```csharp
|
|
public interface IReportService
|
|
{
|
|
Task<IActionResult> ProcessRequestAsync(string action, string id,
|
|
string userAccountId, DatabaseSecurity dbSec);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5.7 `FdsMfrClient` → Injectable MFR Client
|
|
|
|
**Current state:** Created with `new FdsMfrClient()` directly in controller code (e.g., `IntranetController.Invoices2.cs` line 26). Uses static `FdsConfig` for credentials.
|
|
|
|
**Problem:** Cannot be mocked for testing. Credentials hardwired to static config.
|
|
|
|
**Proposed service:**
|
|
```csharp
|
|
public interface IMfrClientFactory
|
|
{
|
|
FdsMfrClient Create();
|
|
}
|
|
|
|
public class MfrClientFactory : IMfrClientFactory, IDisposable
|
|
{
|
|
private readonly ILoggerFactory _loggerFactory;
|
|
private readonly MFRClientCredentials _credentials;
|
|
// Inject ILoggerFactory, IOptions<MfrSettings>
|
|
}
|
|
```
|
|
**Registration:** `builder.Services.AddSingleton<IMfrClientFactory, MfrClientFactory>();`
|
|
|
|
**Files affected:** `FdsMfrClient.cs`, `IntranetController.Invoices2.cs`, any code doing `new FdsMfrClient()`.
|
|
|
|
---
|
|
|
|
### 5.8 `Fuchs_intranet` — Decompose the God Object
|
|
|
|
**Current state:** Single class handling configuration, DB connections, authentication, module auth queries, debug logging, and PDF licensing.
|
|
|
|
**Recommended split:**
|
|
|
|
| Responsibility | Proposed Service | Lifetime |
|
|
|---|---|---|
|
|
| Configuration (conn strings, app settings) | `IOptions<FuchsSettings>` | Singleton |
|
|
| DB connection factory | `IDbConnectionFactory` | Singleton |
|
|
| User authentication | `IAuthenticationService` | Scoped |
|
|
| Module authorization | `IAuthorizationService` (custom) | Scoped |
|
|
| Debug/error logging | Use built-in `ILogger<T>` | — |
|
|
|
|
---
|
|
|
|
## 6. Priority Ranking
|
|
|
|
| Priority | Candidate | Impact | Effort |
|
|
|---|---|---|---|
|
|
| 🔴 **1** | `FuchsFdsEmail` → `IEmailService` | Fixes `ConfigurationManager` violation, high testability gain | Low |
|
|
| 🔴 **2** | `FuchsWidgets` → `IWidgetService` | Removes controller coupling | Low |
|
|
| 🟡 **3** | `FdsMfrClient` → `IMfrClientFactory` | Removes `new` in controllers, enables mocking | Medium |
|
|
| 🟡 **4** | `Banking` → `IBankingService` | Clean DI pattern, minor effort | Low |
|
|
| 🟡 **5** | `FuchsPdf` → `IPdfService` | Large file, significant but high-value refactor | Medium |
|
|
| 🟠 **6** | `FdsInvoiceData`/`FdsReminderData` → Services | Major architectural improvement, most effort | High |
|
|
| 🟠 **7** | `Fuchs_intranet` decomposition | God-object split, foundational but risky | High |
|
|
| 🟢 **8** | `FuchsReports` → `IReportService` | Minimal current logic, prep for future | Low |
|
|
|
|
---
|
|
|
|
## 7. Additional Observations
|
|
|
|
1. ✅ **Resolved** — email/SMS moved off `ConfigurationManager` into `IComService` (ProcessWeb Mailer API).
|
|
2. ✅ **Resolved** — `FdsInvoiceData`/`FdsReminderData` are now pure data holders; DB + PDF logic moved to `IInvoiceService`/`IReminderService`.
|
|
3. ✅ **Resolved** — `FdsMfrClient` is created via `IMfrClientFactory` (no `new` in controllers).
|
|
4. ✅ **Resolved** — `OCORE_Charting` is now used (transitively, via `OCORE_web`'s chart engine) by the report renderer (`FuchsVisualization`).
|
|
5. ⏳ **Open** — **Topshelf** in `Fuchs_DataService` could be replaced with native `dotnet` Worker Service hosting for .NET 10 alignment.
|
|
|
|
---
|
|
|
|
## 8. Observability (OpenTelemetry)
|
|
|
|
- Instrumentation is centralised in `Fuchs/Observability/FuchsTelemetry.cs`: one `ActivitySource` and one `Meter` (`Fuchs.Intranet`).
|
|
- **Metrics** — counters (`fuchs.invoices.rendered`, `fuchs.reminders.rendered`, `fuchs.reports.rendered`, `fuchs.emails.sent`/`.failed`, `fuchs.sms.sent`, `fuchs.banking.mt940.rows`, `fuchs.mfr.calls`) and duration histograms (`fuchs.pdf.render.duration`, `fuchs.report.render.duration`, `fuchs.email.send.duration`).
|
|
- **Tracing** — ASP.NET Core, HttpClient and SqlClient instrumentation plus the app `ActivitySource`; services start spans for their key operations.
|
|
- Configured in `Program.cs`. Always collected in-process; **OTLP export is opt-in** via `Fuchs:Telemetry:OtlpEndpoint` (and can be disabled with `Fuchs:Telemetry:Enabled=false`), so a missing collector never affects the app.
|
|
- All services + handlers log entry/result/timing/errors via `ILogger<T>` with structured placeholders.
|
|
|
|
## 9. Bank Statement Parsing (MT940 + CAMT)
|
|
|
|
- `BankingService` (`IBankingService`) accepts **both** MT940 (SWIFT text, via the external `MT940Parser`) and **CAMT** (ISO 20022 camt.052/053/054 XML, via the in-repo `CAMTParser`).
|
|
- `ParseToDatatable` **auto-detects** the format from content (XML → CAMT, else MT940) and maps either into the `fds__tt__bankingtransactions` schema; the `bam/up` handler and the frontend upload accept both.
|
|
- `CAMTParser` matches elements by **local name** (namespace-agnostic) so it works across every camt schema version. When the banking schema changes, keep the MT940 and CAMT column mappings in `BankingService` aligned.
|