2a75664625
- 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>
82 lines
7.7 KiB
Markdown
82 lines
7.7 KiB
Markdown
# Copilot Instructions
|
|
|
|
> ## ⚠️ Instruction Sync
|
|
> This file (`.github/copilot-instructions.md`) and the Claude Code instructions
|
|
> (`/CLAUDE.md`) are **two views of the same project rules and must stay in sync**.
|
|
> Whenever you change one, make the equivalent change in the other in the same
|
|
> commit. `CLAUDE.md` may add tool-specific workflow notes, but the shared
|
|
> project facts (architecture, coding standards, configuration, libraries,
|
|
> secrets, observability) must match.
|
|
|
|
## Project Overview
|
|
- **Fuchs Intranet** is an ASP.NET Core (.NET 10) web application — the intranet IS the entire website, served from `/`.
|
|
- Routes: `/{fn?}/{id?}/{code?}` → `IntranetController.Index`; `/do/{fn?}/{id?}/{code?}` → `IntranetController.Do`.
|
|
- Project structure (relative to `Fuchs/`):
|
|
- `Controllers/` — `IntranetController` partials (no area)
|
|
- `code/` — business logic, PDF, email, widgets, data models
|
|
- `css/intranet/` — intranet SCSS source files
|
|
- `js/intranet/` — intranet JS source files (modules in `js/intranet/modules/`)
|
|
- `Data/` — static data assets (images for PDF, HTML files)
|
|
- `Views/Intranet/` — Razor views; `Views/Shared/_Layout.cshtml`; `Views/Partials/`
|
|
|
|
## Coding Standards
|
|
- All code must be written in C#.
|
|
- Keep files to a limit of 400 (max 600) lines of code to ensure maintainability and readability. Proactively refactor larger files into smaller, focused classes or components as needed.
|
|
- Follow standard C# naming conventions (PascalCase for classes and methods, camelCase for variables and parameters).
|
|
- Use modern, performance-oriented C# .NET 10 features and best practices, such as async/await for asynchronous programming, LINQ for data manipulation, and dependency injection for better testability and maintainability.
|
|
|
|
## Configuration
|
|
- All application settings live in `Fuchs/appsettings.json` — **do not use `Web.config` or `System.Configuration.ConfigurationManager`**.
|
|
- App-specific settings are nested under the `"Fuchs"` key (e.g., `_config["Fuchs:SMS_APIKey"]`).
|
|
- Connection strings are stored under the standard `"ConnectionStrings"` key and read via `IConfiguration.GetConnectionString(...)`.
|
|
- `FuchsOcmsIntranet.Initialize(configuration)` must be called at app start (in `Program.cs`) before DI registration; `Fuchs_intranet` receives `IConfiguration` via its constructor.
|
|
- `appsettings.Development.json` (git-ignored) can override secrets for local development.
|
|
|
|
## Libraries
|
|
- Do not upgrade Spire.PDF beyond version 8.10.5.
|
|
- Make use of OCORE libraries where possible, especially for common tasks such as logging, configuration management, and data access.
|
|
- Whenever possible, prefer OCORE_web_pdf / OCORE PDF functions for PDF-related tasks over rewriting.
|
|
- Do not use OCMS or OCMS_sharp; use only OCORE or OCORE_web.
|
|
- For builds failing due to SixLabors.ImageSharp requiring a license (v4.0.0+), check copilot-instructions.md for the SixLabors license key/handling info before downgrading ImageSharp.
|
|
|
|
## Services & Dependency Injection
|
|
- Business logic lives in **DI-registered services** under `Fuchs/Services/` behind interfaces; inject them into `IntranetController` (constructor injection). Do **not** reintroduce static God-classes or pass the whole controller into helpers.
|
|
- `IComService` (email/SMS via ProcessWeb Mailer API, attachments sent inline as base64), `IPdfService` (MigraDoc render), `IInvoiceService`, `IReminderService`, `IReportService` (SQL report engine via `FuchsVisualization`), `IWidgetService`, `IBankingService`, `IMfrClientFactory`.
|
|
- Lifetimes: stateless services (`IPdfService`, `IBankingService`, `IMfrClientFactory`) are singletons; request-scoped DB services (`IInvoiceService`, `IReminderService`, `IReportService`, `IWidgetService`, `IComService`) are scoped. Register in `Program.cs`.
|
|
- `FdsInvoiceData` / `FdsReminderData` are **pure data holders** (parse + properties). Loading, persistence and PDF generation belong in the services — never `Task.Run(...).Wait()` sync-over-async.
|
|
- Data access stays SQL-first via OCORE helpers (`getSQLDataSet_async`, `setSQLValue_async`) + stored procedures; no EF Core.
|
|
|
|
## MFR ERP integration
|
|
- `MFR_RESTClient` talks to the **mfr (Mobile Field Report)** ERP over REST/OData. Its contract (base URLs, auth, OData conventions, pagination, error/retry, deep-create + document-upload) is documented in **`MFR_RESTClient/Docs/mfr_interface_description.md`** — **read it before changing the client**.
|
|
- The client uses HTTP Basic auth, a configurable timeout, and retries idempotent GETs on transient errors (429/5xx, network/timeout) with backoff. Create clients via `IMfrClientFactory` (don't `new` them). The legacy VB project files have been removed; the active project is `MFR_RESTClient.csproj`.
|
|
|
|
## Database
|
|
- The SQL schema source of truth is the **`Fuchs_Database`** SSDT project. The backend is SQL-first (stored procedures, table types like `fds__tt__bankingtransactions`, functions via OCORE helpers — no EF Core).
|
|
- When you change a stored proc name/params or a table type, update **both** the SSDT project and the calling C# in the same change. Verify every `[dbo].[…]` the backend calls actually exists in `Fuchs_Database`.
|
|
|
|
## Bank statement parsing (MT940 + CAMT)
|
|
- Two parsers feed the same banking pipeline: the external `MT940Parser` (SWIFT text) and the in-repo **`CAMTParser`** project (ISO 20022 camt.052/053/054 XML).
|
|
- `BankingService.ParseToDatatable` **auto-detects** the format (XML → CAMT, else MT940) and maps both into the `fds__tt__bankingtransactions` schema. The `bam/up` handler and the frontend file picker accept both (`.sta/.mt940/.txt` and `.xml/.camt`).
|
|
- `CAMTParser` is namespace-agnostic (matches elements by local name) so it handles every camt schema version. Keep both parsers' column mappings aligned when changing the banking schema.
|
|
|
|
## Observability
|
|
- Use **OpenTelemetry**. The app's instrumentation is centralised in `Fuchs/Observability/FuchsTelemetry.cs` (one `ActivitySource` + one `Meter`).
|
|
- When adding a meaningful operation: start an activity (`FuchsTelemetry.StartActivity(...)`), record the relevant counter/histogram, and log entry/result/timing/errors via the injected `ILogger<T>`. Prefer structured logging (named placeholders), never string interpolation in log messages.
|
|
- Tracing/metrics are always collected; OTLP export is opt-in via `Fuchs:Telemetry:OtlpEndpoint`. Don't add exporters that fail hard when no collector is present.
|
|
|
|
## Testing
|
|
- xUnit in `Fuchs.Tests`. For every service/handler change add tests covering **both** an intentionally succeeding and an intentionally failing path where feasible (use stubs/mocks; the test project has `InternalsVisibleTo`). DB-bound paths that can't be unit-tested should at least have their pure logic covered.
|
|
|
|
## Azure Key Vault — Secret Naming
|
|
- Secret names must satisfy the pattern `^[0-9a-zA-Z-]+$` (alphanumerics and hyphens only; no underscores, dots, or spaces).
|
|
- Hierarchy levels are separated by `--` (double hyphen), which maps to `:` in `IConfiguration`.
|
|
- Underscores within a name segment are encoded as a single `-` in Key Vault and decoded back to `_` when the key is reconstructed.
|
|
- The app prefix `fuchs` is prepended to every secret name.
|
|
- Format: `{appname}--{Section}--{key-with-hyphens-for-underscores}`
|
|
- Examples:
|
|
- `fuchs--ConnectionStrings--ocms-ConnectionString` → `ConnectionStrings:ocms_ConnectionString`
|
|
- `fuchs--Fuchs--SMS-APIKey` → `Fuchs:SMS_APIKey`
|
|
- `fuchs--Fuchs--Email--Main--password` → `Fuchs:Email:Main:password`
|
|
- When adding new secrets: replace every `_` in the original config key with `-` for the Key Vault name, and add the entry to `ManagedSecretKeys` in `appsettings.json` (using the same hyphenated form without the `fuchs--` prefix).
|
|
|