# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Overview Proprietary Windows Forms (.NET 9) desktop tool for **StudioU**, an online photo-print service. It turns WooCommerce order exports into print-ready file packages, product catalogs, printable labels, and emailed digital downloads. UI text, comments, and CSV column names are predominantly **Czech** — match that when editing user-facing strings. ## Build / Test / Run ```powershell # Build whole solution dotnet build qdr.app.studiou.orders2printpack.sln # Run the app (WinExe Windows Forms — Windows only) dotnet run --project App # Run all tests (NUnit) dotnet test # Run a single test fixture or test dotnet test --filter "FullyQualifiedName~EmailStorageProcessorTests" dotnet test --filter "Name=MethodNameHere" ``` Notes: - App targets `net9.0-windows7.0`; Test targets `net9.0-windows`. `Nullable` and `ImplicitUsings` are enabled in both. - The App assembly is **signed** with `bo_strong_key_pair.snk` (referenced as `..\bo_strong_key_pair.snk`). - `Setup/Setup.vdproj` is a Visual Studio Installer project — only buildable in Visual Studio with the installer-projects extension, not via `dotnet`. Bump `Version`/`AssemblyVersion`/`FileVersion` in `App/...csproj` and the README changelog together when releasing (see commit history convention). ### External (NuGet) dependencies that aren't in this repo - `qdr.fnd.core` (Quadarax.Foundation.Core) — shared foundation: `ILogger`/`ILog` logging, `FileSearch`, `FileUtils`, `CsvHelper`/`CsvUtils` (DataTable CSV), and `ParameterizedString` (the `{token}` substitution engine used for output filenames). Source is not in this repo; treat as an opaque library. - `qdr.app.tss.client` — REST client for Quadarax **Temporary Shared Storage (TSS)**; used to upload ZIPs and get a download permalink (`FileUploadService`, `UploadFileRequest`). - `DocumentFormat.OpenXml` — used to generate `.xlsx` label sheets without Office installed. ## Architecture `Program.cs` launches `FormLauncher`, a hub with buttons opening four independent workflows plus a config editor. Each workflow is a self-contained `Form` with its own logic — there is no shared controller/service layer; forms read/write the shared `AppSettings` (user settings) directly. The four workflows: 1. **FormBatch** — the core "orders → print package" flow. Loads a WooCommerce order CSV into a `DataTable`, scans a source RAW-image folder (`FileSearch`), matches each order line's image URL to a local file by filename, then copies/renames files into the output folder. Output paths are built from `AppSettings.OutputFileMask` via `ParameterizedString` (`{order_no}`, `{prod_name}`, `{ordinal}`, `{ext}`, plus every CSV column normalized to lower_snake_case). Quantity expands to multiple numbered copies. Lines whose format matches `FormatDigitalContains` **and** have an email are routed into a per-email subfolder (the "digital" path) instead of print. Can also emit a protocol CSV (`GenerateProtocol`) stamping external order number/date. 2. **FormProductEdit** + `ProductStorage/` — edit/import the product catalog. `ProductStorage` holds a flat list of `ProductDto` rows where a row is either a product (`Type == "variable"`) or a variant (`Type == "variation"`, linked to parent by SKU). Variants and categories are derived views over that list. `ProductDto.IsValid` encodes the catalog validity rules. Import builds URLs/descriptions from `AppSettings.ImportTpl*` templates. 3. **FormLabelPrint** + `Labels/ExcelLabelGenerator.cs` — CSV → printable `.xlsx` label sheet. `ProcessData` first aggregates rows (group-by `LabelAggregateColumns`, building computed columns via `LabelAggegationDataExpression`). `ExcelLabelGenerator` lays labels out in an N×M grid per A4 page using OpenXML, expanding `{columnName}` placeholders and a lightweight markup in `LabelTemplate`: `**bold**`, `__italic__`, `_underline_`, `^^size^text^^`, and `{cr}` → newline. 4. **FormDigitalSend** + `EmailStorage/EmailStorageProcessor.cs` — emails download links for digital orders. `EmailStorageProcessor` is the substantive class here (FormDigitalSend itself is largely a stub). It treats each subdirectory of an input folder as one recipient (directory name = email address), and drives a **state machine** persisted in `metadata.json`: `Ready → Processing → Uploading → Sending → Sent` (or `Error`). Per item it zips the JPGs, uploads to TSS, generates a download password + permalink, personalizes the email template (`{{DOWNLOAD_LINK}}`, `{{PASSWORD}}`, `{{RECIPIENT}}`, `{{ORDERS}}`), sends via SMTP, and moves the ZIP through hidden dirs `.processing/` → `.sending/` → `.sent/`. Order numbers are parsed from filenames matching `^(\d+)_.*\.jpg$`. ### CSV handling — two distinct parsers - **`CsvParser`** (`App/Extensions/CsvParserUtility.cs`) — reflection/attribute-based; maps CSV columns to typed properties via `[CsvColumn("Header Name")]`. Used for the strongly-typed product catalog (`ProductDto`). - **`CsvHelper` / `CsvUtils`** (DataTable-based, mostly from `qdr.fnd.core`) — used by FormBatch and FormLabelPrint where columns are dynamic and addressed by name at runtime. Both respect `AppSettings.CSVDelimiter`. ### Configuration (`AppSettings`) All operational config lives in the generated `Properties/AppSettings` user-settings class (`AppSettings.settings`), edited at runtime through `dlgSettings` / `dlgEditCfg`. Key groups: `MapCol*` (map logical fields to actual CSV column names — change these, not the code, when an export's headers differ), output/import templates, label layout + template, TSS credentials (`TSSApiKey`/`TSSUri`/`TSSChunkSizeBytes`), and SMTP. "Reset config" on the launcher restores defaults. ### Testability convention File I/O goes through `System.IO.Abstractions` (`IFileSystem`), so logic is unit-testable with `MockFileSystem`. New file-touching logic should take/use `IFileSystem` rather than `System.IO.File`/`Directory` directly. Tests use **NUnit + Moq + System.IO.Abstractions.TestingHelpers**; `EmailStorageProcessorTests` is the model to follow.