Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/SKILL.md

16 KiB
Raw Blame History

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.ProductBoxAddinfoBeforeProductAIWidgetViewComponent
  • PublicWidgetZones.ProductDetailsBottomProductAIWidgetViewComponent
  • AdminWidgetZones.ProductDetailsBlockProductAttributesViewComponent
  • AdminWidgetZones.OrderDetailsBlockOrderAttributesViewComponent

8. DI Registration Patterns (PluginNopStartup)

Important overrides / replacements:

// Replaces the default NopCommerce price calculator
services.Replace(ServiceDescriptor.Scoped<IPriceCalculationService, CustomPriceCalculationService>());

// Overrides email order table rendering
services.Replace(ServiceDescriptor.Scoped<IMessageTokenProvider, FruitBankMessageTokenProvider>());

// Overrides generic attribute service
services.AddScoped<IGenericAttributeService, GenericAttributeService>();

// Overrides order model and product model factories
services.AddScoped<IOrderModelFactory, CustomOrderModelFactory>();
services.AddScoped<IProductModelFactory, CustomProductModelFactory>();

// Overrides WorkflowMessageService (order emails)
services.AddScoped<IWorkflowMessageService, WorkflowMessageService>();

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):

--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 <li class="quick-order-menu-item"> 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: <Attributes><CustomerAttribute ID="1"><CustomerAttributeValue><Value>...</Value>...
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)