Files
PowderCoatingLogix/src/PowderCoating.Application/DTOs/Company/CustomItemTemplateDtos.cs
T
spouliot 1eba50cf0f Add Custom Formula Item Templates with AI generation and wizard integration
Introduces per-company reusable NCalc2 pricing formula templates for complex
fabricated items (roof curbs, enclosures, welded frames). Templates support
two output modes — FixedRate (formula yields a dollar amount) and SurfaceAreaSqFt
(formula yields sq ft fed into the standard coating engine). Includes:

- CustomItemTemplate entity, migration (AddCustomItemTemplates), IUnitOfWork repo
- IsCustomFormulaItem / CustomItemTemplateId / FormulaFieldValuesJson flags on
  QuoteItem, JobItem, CreateQuoteItemDto; mapped in all 3 JobItemAssemblyService
  overloads and all existingItemsData JSON projections + pageMeta blocks
- ICustomFormulaAiService / CustomFormulaAiService: Claude-powered formula
  generator (natural language + optional diagram image) and NCalc2 evaluator
- CompanySettings CRUD endpoints: GetCustomItemTemplates, Create/Update/Delete,
  UploadTemplateDiagram, TemplateDiagram (blob serve), EvaluateFormula, GenerateFormulaFromAi
- Company Settings "Custom Formulas" tab + cfModal + company-settings-custom-formulas.js
- item-wizard.js: formula item type card, renderFormulaFields, wzFormulaRecalc
  (live evaluate via POST), collectStep2 formula branch, buildCardHtml / emitHiddenFields
- Formula badge in Quotes/Details and Jobs/Details; AI badge gap fixed in Jobs/Details
- Help article (CustomFormulaTemplates.cshtml), Help Index card, HelpController action,
  HelpKnowledgeBase entry; 225/225 unit tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 15:09:22 -04:00

175 lines
5.8 KiB
C#

using System.ComponentModel.DataAnnotations;
namespace PowderCoating.Application.DTOs.Company;
// ============================================================================
// LIST DTO - For Company Settings tab table
// ============================================================================
public class CustomItemTemplateListDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public string OutputMode { get; set; } = "FixedRate";
public int FieldCount { get; set; }
public decimal? DefaultRate { get; set; }
public string? RateLabel { get; set; }
public bool IsActive { get; set; }
public int DisplayOrder { get; set; }
public string? DiagramImagePath { get; set; }
}
// ============================================================================
// FULL DTO - For Edit modal and formula evaluation
// ============================================================================
public class CustomItemTemplateDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public string OutputMode { get; set; } = "FixedRate";
public string FieldsJson { get; set; } = "[]";
public string Formula { get; set; } = string.Empty;
public decimal? DefaultRate { get; set; }
public string? RateLabel { get; set; }
public string? Notes { get; set; }
public int DisplayOrder { get; set; }
public bool IsActive { get; set; }
public string? DiagramImagePath { get; set; }
}
// ============================================================================
// CREATE DTO
// ============================================================================
public class CreateCustomItemTemplateDto
{
[Required]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[StringLength(500)]
public string? Description { get; set; }
/// <summary>"FixedRate" or "SurfaceAreaSqFt"</summary>
[Required]
public string OutputMode { get; set; } = "FixedRate";
/// <summary>JSON array of field definitions: [{name, label, unit, defaultValue}]</summary>
[Required]
public string FieldsJson { get; set; } = "[]";
[Required]
public string Formula { get; set; } = string.Empty;
public decimal? DefaultRate { get; set; }
[StringLength(50)]
public string? RateLabel { get; set; }
[StringLength(1000)]
public string? Notes { get; set; }
public int DisplayOrder { get; set; }
public bool IsActive { get; set; } = true;
}
// ============================================================================
// UPDATE DTO
// ============================================================================
public class UpdateCustomItemTemplateDto
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[StringLength(500)]
public string? Description { get; set; }
[Required]
public string OutputMode { get; set; } = "FixedRate";
[Required]
public string FieldsJson { get; set; } = "[]";
[Required]
public string Formula { get; set; } = string.Empty;
public decimal? DefaultRate { get; set; }
[StringLength(50)]
public string? RateLabel { get; set; }
[StringLength(1000)]
public string? Notes { get; set; }
public int DisplayOrder { get; set; }
public bool IsActive { get; set; } = true;
/// <summary>Existing diagram path — kept if no new file is uploaded.</summary>
public string? DiagramImagePath { get; set; }
}
// ============================================================================
// WIZARD PICKER DTO - Lean DTO for populating the quote wizard template list
// ============================================================================
public class CustomItemTemplatePickerDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public string OutputMode { get; set; } = "FixedRate";
public string FieldsJson { get; set; } = "[]";
public string Formula { get; set; } = string.Empty;
public decimal? DefaultRate { get; set; }
public string? RateLabel { get; set; }
public string? DiagramImagePath { get; set; }
}
// ============================================================================
// AI GENERATION DTOs
// ============================================================================
public class GenerateFormulaFromAiRequest
{
[Required]
public string Description { get; set; } = string.Empty;
}
public class GenerateFormulaFromAiResponse
{
public bool Success { get; set; }
public string? Error { get; set; }
public string? Name { get; set; }
public string? OutputMode { get; set; }
public string? FieldsJson { get; set; }
public string? Formula { get; set; }
public decimal? DefaultRate { get; set; }
public string? RateLabel { get; set; }
public string? Reasoning { get; set; }
/// <summary>Result of running the formula with any sample values found in the description.</summary>
public decimal? VerificationResult { get; set; }
public string? VerificationInputs { get; set; }
}
// ============================================================================
// FORMULA EVALUATION DTOs
// ============================================================================
public class EvaluateFormulaRequest
{
[Required]
public string Formula { get; set; } = string.Empty;
/// <summary>JSON object of variable name → value pairs, e.g. {"box_l": 43, "rate": 0.05}</summary>
[Required]
public string VariablesJson { get; set; } = "{}";
}
public class EvaluateFormulaResponse
{
public bool Success { get; set; }
public decimal? Result { get; set; }
public string? Error { get; set; }
}