# FruitBank Plugin – Claude Skill Reference > **Purpose:** This file is a reference document for Claude to quickly understand the FruitBank NopCommerce plugin codebase, patterns, and conventions so that new work sessions can ramp up without re-reading the entire codebase from scratch. --- ## 1. Project Identity | Property | Value | |---|---| | Plugin system name | `Misc.FruitBankPlugin` | | DLL | `Nop.Plugin.Misc.FruitBankPlugin.dll` | | Namespace root | `Nop.Plugin.Misc.FruitBankPlugin` | | NopCommerce version | **4.80** | | Author | Adam Gelencser | | Plugin source path | `D:\REPOS\MANGO\source\Nopcommerce.Common\4.70\Plugins\Nop.Plugin.Misc.AIPlugin` | | Theme path | `D:\REPOS\MANGO\source\FruitBank\Presentation\Nop.Web\Themes\CarHaven` | The plugin is called **AIPlugin** on disk (folder/csproj) but the assembly and namespace use `FruitBankPlugin`. Both names are the same thing. --- ## 2. Business Domain FruitBank is a **Hungarian fruit and vegetable wholesale company** running a private B2B NopCommerce webshop. The typical user is a warehouse employee or admin working on mobile. Key business concepts: - **Partners** – business customers (companies), matched by name across multiple systems - **Shipping documents** – PDF/image documents received from suppliers, parsed by AI - **Measurable products** – products that require physical weighing before price is finalized; `IsMeasurable` is determined server-side only - **Stock taking** – periodic inventory audit workflow with discrepancy reports - **InnVoice** – external accounting/invoicing system, synced via `InnVoiceOrderService` / `InnVoiceApiService` - **Voice ordering** – warehouse staff dictate orders in Hungarian; transcribed via Whisper --- ## 3. Folder Structure ``` Nop.Plugin.Misc.AIPlugin/ ├── Areas/Admin/ │ ├── Controllers/ # All admin-area controllers │ ├── Components/ # Admin view components │ ├── Factories/ # CustomOrderModelFactory, CustomProductModelFactory │ ├── Models/ # Admin view models (extended Nop models) │ ├── Validators/ │ └── Views/ # Admin Razor views; custom layouts: _FruitBankAdminLayout.cshtml ├── Controllers/ # Public-facing controllers (QuickOrder, Checkout, FruitBankData) ├── Components/ # Widget view components (ProductAI, ProductAttributes, OrderAttributes) ├── css/ / js/ # Static assets for the plugin ├── Domains/ │ └── DataLayer/ # LinqToDB table classes + DbContexts │ ├── FruitBankDbContext.cs │ ├── StockTakingDbContext.cs │ └── *DbTable.cs # One file per custom table ├── Infrastructure/ │ ├── PluginNopStartup.cs # DI registration + SignalR + middleware │ ├── RouteProvider.cs │ ├── ViewLocationExpander.cs │ └── FruitBankMessageTokenProvider.cs # Overrides IMessageTokenProvider ├── Services/ # Business logic services ├── Localization/ │ ├── quickorder.en.xml │ └── quickorder.hu.xml ├── FruitBankPlugin.cs # Main plugin class (IWidgetPlugin) ├── FruitBankSettings.cs # Plugin settings (ApiKey etc.) ├── FruitBankConst.cs # Constants └── plugin.json ``` --- ## 4. Key Services ### AI / LLM | Service | Purpose | |---|---| | `OpenAIApiService` | Primary OpenAI integration – chat completions, Whisper transcription | | `OpenAiService` | Lightweight wrapper using `gpt-4o-mini` for simple prompts | | `CerebrasAPIService` | Alternative LLM provider | | `ReplicateService` | Replicate.com API (image/audio models); registered with a hardcoded Bearer token | | `AICalculationService` | AI-assisted price/measurement calculations | ### Storage | Service | Purpose | |---|---| | `FileStorageService` | Generic file storage: SHA256 hash dedup, GZip compression, path building | | `IFileStorageProvider` / `LocalFileStorageProvider` | Strategy pattern storage backend (currently local disk / wwwroot/uploads) | **FileStorageService patterns:** - Calculates SHA256 on upload BEFORE any AI processing → prevents duplicate API calls - Skips GZip for already-compressed formats (jpg, pdf, mp4, zip, etc.) - Path format: `{userId}/{featureName}/{entityType}-{entityId}/{fileName}_{id}.ext` - DB record created first to get ID, then file is saved; rolled back on failure ### Order / Measurement | Service | Purpose | |---|---| | `OrderMeasurementService` / `IOrderMeasurementService` | Handles orders that contain measurable products | | `MeasurementService` / `IMeasurementService` | Core weighing logic | | `InnVoiceOrderService` | Syncs orders with InnVoice accounting system | | `InnVoiceApiService` | HTTP client for the InnVoice REST API | ### Infrastructure | Service | Purpose | |---|---| | `FruitBankAttributeService` | Custom product/order attribute helpers | | `LockService` / `ILockService` | Singleton distributed lock | | `PdfToImageService` | Converts PDF pages to images for AI vision processing | | `EventConsumer` | Handles `OrderPlacedEvent` | | `FruitBankHub` | SignalR hub for real-time admin notifications | --- ## 5. Admin Controllers | Controller | Purpose | |---|---| | `CustomOrderController` | Extended order management: **split order** feature (audit-based + manual selection modes), order notes, SignalR events | | `CustomDashboardController` | AI-powered admin dashboard with `GetWelcomeMessageAsync` (store summary, order totals, stock discrepancies, OpenWeatherMap weather) | | `ShippingController` | Shipping document management + AI PDF extraction workflow | | `VoiceOrderController` | Voice-to-order admin tool (mobile-optimized) | | `FruitBankAudioController` | Audio upload/processing endpoint for Whisper transcription | | `InvoiceController` | Invoice generation and management | | `InnVoiceOrderController` | InnVoice order sync UI | | `InnVoiceOrderSyncController` | InnVoice sync API endpoints | | `ManagementPageController` | General management page | | `FileManagerController` + `FileManagerScriptsApiController` | File manager UI | | `FileStorageController` | File storage API endpoints | | `AppDownloadController` | App download/distribution page | | `FruitBankPluginAdminController` | Plugin configuration page | | `CustomProductController` | Extended product admin (IsMeasurable etc.) | --- ## 6. Public Controllers | Controller | Route | Purpose | |---|---|---| | `QuickOrderController` | `/gyors-rendeles` | Customer-facing quick order page with voice + text search | | `CheckoutController` | `/checkout/*` | Custom checkout flow override | | `FruitBankDataController` | `/fruitbankdata/*` | Public data API endpoints; also implements `IFruitBankDataControllerServer` | --- ## 7. Widget Zones The plugin registers widgets in: - `PublicWidgetZones.ProductBoxAddinfoBefore` → `ProductAIWidgetViewComponent` - `PublicWidgetZones.ProductDetailsBottom` → `ProductAIWidgetViewComponent` - `AdminWidgetZones.ProductDetailsBlock` → `ProductAttributesViewComponent` - `AdminWidgetZones.OrderDetailsBlock` → `OrderAttributesViewComponent` --- ## 8. DI Registration Patterns (PluginNopStartup) Important overrides / replacements: ```csharp // Replaces the default NopCommerce price calculator services.Replace(ServiceDescriptor.Scoped()); // Overrides email order table rendering services.Replace(ServiceDescriptor.Scoped()); // Overrides generic attribute service services.AddScoped(); // Overrides order model and product model factories services.AddScoped(); services.AddScoped(); // Overrides WorkflowMessageService (order emails) services.AddScoped(); ``` SignalR is configured with: - MaximumReceiveMessageSize / StatefulReconnectBufferSize: 30 MB - `DevAdminSignalRHub` on `/{FruitBankConstClient.DefaultHubName}` (WebSockets only) - `LoggerSignalRHub` on `/{FruitBankConstClient.LoggerHubName}` --- ## 9. Database / Data Layer Custom tables use **LinqToDB** (not EF Core) through wrapper DbTable classes registered in DI. Two DbContext wrappers: - `FruitBankDbContext` – main plugin data - `StockTakingDbContext` – stock taking workflow data Key custom tables: | DbTable class | Purpose | |---|---| | `PartnerDbTable` | Business partners (wholesale customers) | | `ShippingDbTable` | Shipping records | | `ShippingDocumentDbTable` | Parsed shipping document metadata | | `ShippingItemDbTable` | Line items from shipping documents | | `ShippingDocumentToFilesDbTable` | Junction: document ↔ file | | `FilesDbTable` | Generic file records (hash, compression flag, raw text) | | `OrderDtoDbTable` / `OrderItemDtoDbTable` | Order DTO projections | | `OrderItemPalletDbTable` / `ShippingItemPalletDbTable` / etc. | Pallet tracking for measurement workflow | | `StockTakingDbTable` / `StockTakingItemDbTable` | Stock audit records | | `StockQuantityHistoryDtoDbTable` | Stock movement history | | `MeasuringItemPalletBaseDbTable` | Base pallet measuring data | **N+1 query prevention:** Always batch DB calls with `Task.WhenAll`. Never query per-item inside a loop. --- ## 10. Localization All resource keys follow the prefix: `Plugins.Misc.FruitBankPlugin.*` - Keys are registered programmatically in `FruitBankPlugin.InstallAsync()` for **both EN and HU** - XML locale files in `/Localization/`: `quickorder.en.xml`, `quickorder.hu.xml` - When adding new keys: update **all three places** (InstallAsync + both XML files) - Hungarian is the **primary** language; English is secondary - Use `_localizationService.AddOrUpdateLocaleResourceAsync("key", "value", "HU")` pattern Common key prefixes: - `Plugins.Misc.FruitBankPlugin.Menu.*` – navigation - `Plugins.Misc.FruitBankPlugin.QuickOrder.*` – quick order page (extensive set) --- ## 11. Quick Order Page (`/gyors-rendeles`) **Controller:** `QuickOrderController` **View:** `/Views/QuickOrder/` **CSS:** `/css/quick-order.css` (in plugin) + deployed to CarHaven theme Design system tokens (CarHaven theme): ```css --theme-color: #2d7a3a /* green */ --active-color: #f4a236 /* amber */ --dark: #1a3c22 --light-bg: #f5f7f2 font-family: 'DM Sans' border-radius: 8px ``` Product cards use full-width flex rows: `.product-card { flex-direction: row }` with `.pc-body` (left, grows) and `.pc-actions` (right, fixed). Navigation menu integration: - CarHaven `TopMenu/Default.cshtml` has a `
  • ` for both desktop (`.notmobile`) and mobile (`.mobile`) menu blocks - Guarded by `@if (Model.DisplayCustomerInfoMenuItem)` (login-gated) - Menu item styled in `quick-order-menu.css` (amber, bold) included via `Head.cshtml` - Uses `fa fa-bolt` icon Voice input: - Records audio in browser, POSTs to `FruitBankAudioController` - Whisper transcription with Hungarian vocabulary hints (partner names + produce terms) - **Prompt character limit is 224** – use keyword extraction, not full company names - Fallback: manual text search input --- ## 12. Split Order Feature Admin page on order detail. Two modes selectable via radio buttons: | Mode | Behaviour | |---|---| | **Audit-based** | Available only when order has both "started" and "non-started" audit items. Audited items stay; non-audited items move to new order. | | **Manual selection** | Always available (except for fully audited orders). Checkbox per item; user chooses what moves. | Split button is always enabled (except audited orders). Mode availability is communicated visually if a mode is disabled. **Critical lesson:** `TransactionSafeAsync` caused deadlocks because `TaskHelper.ToThreadPoolTask` creates async/await context switching in ASP.NET. The transaction wrapper was removed. Avoid wrapping split logic in `TransactionSafeAsync`. After split: inventory adjustments, order notes written, SignalR notification sent to admin clients. --- ## 13. AI Admin Dashboard (`GetWelcomeMessageAsync`) Located in `CustomDashboardController`. Generates a structured OpenAI prompt containing: - Store data summary - Today's order totals - Stock discrepancy summary (from stock taking audit) - OpenWeatherMap weather data (real API key configured in settings) Patterns used: - Typed C# records for data transfer - `Task.WhenAll` for parallel DB calls - Batched product history queries (no N+1) - Bilingual (Hungarian/English) system prompt with JSON field guide for the AI - `salesAdjustmentSum` – be careful not to double-count --- ## 14. Shipping Document Processing AI-driven workflow for extracting partner + product data from uploaded PDFs/images: 1. File uploaded → SHA256 hash calculated **before AI call** 2. Hash checked against DB → if duplicate, load existing data (skip AI, save API cost) 3. PDF converted to image(s) via `PdfToImageService` if needed 4. OpenAI vision API extracts structured product/partner data 5. Multi-stage matching: string search → historical shipping data → AI semantic match 6. UI shows visual matched/unmatched indicators + autocomplete for manual correction 7. `IsMeasurable` is **server-side only** – never expose in frontend forms --- ## 15. NopCommerce 4.80 Gotchas | Issue | Correct approach | |---|---| | `ICustomerAttributeService` does not exist | Use direct `XDocument.Parse` on the XML stored in `GenericAttribute.Value` | | `ParseAttributeValuesAsync` returns empty for free-text attributes | It's designed for predefined selection attributes (ID lookup). For free-text: parse XML directly: `......` | | `TransactionSafeAsync` + async = deadlock | `TaskHelper.ToThreadPoolTask` inside it causes context switching deadlocks in ASP.NET; remove transaction wrapper for affected code | | Email order table customization | Override `IMessageTokenProvider` with `FruitBankMessageTokenProvider` (already done); base class constructor has many parameters – pass all through exactly | | `OrderPlaced.CustomerNotification` email template | Configured in NopCommerce admin under Content → Message Templates; code hook is `WorkflowMessageService.SendOrderPlacedCustomerNotificationAsync` | --- ## 16. Theme (CarHaven) Path: `D:\REPOS\MANGO\source\FruitBank\Presentation\Nop.Web\Themes\CarHaven` Relevant files modified by this plugin's work: - `TopMenu/Default.cshtml` – quick order nav item added - `Head.cshtml` – includes `quick-order-menu.css` - `css/quick-order-menu.css` – amber nav item styling --- ## 17. External APIs / Credentials | Service | Usage | Notes | |---|---|---| | OpenAI | Chat completions (gpt-4o-mini / gpt-4o), Whisper transcription | API key in `FruitBankSettings.ApiKey` | | OpenWeatherMap | Weather data for dashboard welcome message | Real API key confirmed in settings | | Replicate | Image/audio AI models | Bearer token hardcoded in `PluginNopStartup` HTTP client registration | | InnVoice | Hungarian accounting/invoicing system | REST API via `InnVoiceApiService` | --- ## 18. Conventions & Patterns to Follow 1. **Always bilingual** – every new locale resource key goes into InstallAsync (EN + HU) and both XML files. 2. **Mobile-first** for warehouse tools – large touch targets, step-by-step UX, pulse animations. 3. **Server-side business rules** – never expose `IsMeasurable` or similar computed flags in frontend forms. 4. **Batch DB calls** – use `Task.WhenAll`, never query inside a loop. 5. **Hash before AI** – always deduplicate files by SHA256 hash before calling any AI API. 6. **CarHaven design tokens** – use CSS variables (`--theme-color`, `--active-color`, `--dark`, `--light-bg`, `DM Sans` font) consistently. 7. **No `TransactionSafeAsync`** on code paths that use async/await through thread pool. 8. **Expect corrections** – Adam knows the codebase deeply; treat any correction as authoritative and apply it without re-litigating. --- *Last updated: 2026-03-18 | Maintained by: Claude (auto-generated from codebase + project chat history)*