1eba50cf0f
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>
175 lines
5.8 KiB
C#
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; }
|
|
}
|