Files
PowderCoatingLogix/src/PowderCoating.Core/Entities/CompanyPreferences.cs
T
spouliot d5ad9fa073 Add KioskIntakeOutput company setting and fix kiosk submission bugs
- New CompanyPreferences.KioskIntakeOutput setting ("Quote" default / "Job"): controls
  what the kiosk creates on submission; shown as a card-style radio toggle in
  Company Settings → Kiosk tab
- KioskSession.LinkedQuoteId added so quote-first sessions link back to the draft quote
- Migration AddKioskIntakeOutputSetting applies both schema changes
- ProcessSubmissionAsync branches on setting: creates Draft quote (quote-first) or
  Pending job (job-first); save order fixed (CompleteAsync before using DB-assigned Id as FK)
- Terms.cshtml pricing paragraph is now dynamic: "subject to formal quote" for Quote mode,
  "team member will reach out about pricing" for Job mode
- Customer Intakes list: "View Quote" button appears when LinkedQuoteId is set
- Notification label fixed: Remote sessions now say "Remote Intake", not "Walk-in Intake"
- Inactivity reset shortened to 45 s on intake steps
- Signature pad: hosted locally (no CDN), canvas resize deferred via requestAnimationFrame
- AI photo upload: client-side compression to ≤1200px + AbortController 120 s timeout
- Help article and AI knowledge base updated with kiosk feature

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 22:35:37 -04:00

116 lines
5.9 KiB
C#

namespace PowderCoating.Core.Entities;
/// <summary>
/// Company-specific application and workflow preferences
/// </summary>
public class CompanyPreferences : BaseEntity
{
public int CompanyId { get; set; }
// Application Defaults
public string DefaultCurrency { get; set; } = "USD";
public string DefaultDateFormat { get; set; } = "MM/dd/yyyy";
public string DefaultTimeFormat { get; set; } = "12h";
public string DefaultPaymentTerms { get; set; } = "Net 30";
public int DefaultQuoteValidityDays { get; set; } = 30;
public string QuoteNumberPrefix { get; set; } = "QT";
public string JobNumberPrefix { get; set; } = "JOB";
public string InvoiceNumberPrefix { get; set; } = "INV";
public bool UseMetricSystem { get; set; } = false; // False = Imperial (ft, lb), True = Metric (m, kg)
// Job / Workflow Defaults
public string DefaultJobPriority { get; set; } = "Normal";
public bool RequireCustomerPO { get; set; } = false;
public bool AllowCustomerApproval { get; set; } = true;
public int DefaultTurnaroundDays { get; set; } = 7;
// Email Sender Identity
/// <summary>From address used in outgoing emails. Falls back to SendGrid:FromEmail in appsettings when null.</summary>
public string? EmailFromAddress { get; set; }
/// <summary>From display name used in outgoing emails. Falls back to SendGrid:FromName in appsettings when null.</summary>
public string? EmailFromName { get; set; }
// Notifications & Alerts
public bool EmailNotificationsEnabled { get; set; } = true;
public bool NotifyOnNewJob { get; set; } = true;
public bool NotifyOnNewQuote { get; set; } = true;
public bool NotifyOnJobStatusChange { get; set; } = true;
public bool NotifyOnQuoteApproval { get; set; } = true;
public bool NotifyOnPaymentReceived { get; set; } = true;
public int QuoteExpiryWarningDays { get; set; } = 3;
public int DueDateWarningDays { get; set; } = 2;
public int MaintenanceAlertDays { get; set; } = 7;
// Payment Reminders
/// <summary>When true, the background service will send overdue payment reminder emails.</summary>
public bool PaymentRemindersEnabled { get; set; } = false;
/// <summary>Comma-separated days-past-due thresholds at which reminders are sent (e.g. "7,14,30").</summary>
public string PaymentReminderDays { get; set; } = "7,14,30";
// Data Retention
public int QuoteRetentionYears { get; set; } = 7;
public int JobRetentionYears { get; set; } = 7;
public int LogRetentionDays { get; set; } = 90;
public int AutoArchiveJobsDays { get; set; } = 365;
public int DeletedRecordRetentionDays { get; set; } = 30;
// Quote PDF Template
public string QtAccentColor { get; set; } = "#374151";
public string? QtDefaultTerms { get; set; }
public string? QtFooterNote { get; set; }
// Invoice PDF Template
public string InAccentColor { get; set; } = "#374151";
public string? InDefaultTerms { get; set; }
public string? InFooterNote { get; set; }
// Blank Work Order PDF Template
public string WoAccentColor { get; set; } = "#374151";
public string? WoTerms { get; set; }
// Setup Wizard Progress
public bool SetupWizardStarted { get; set; } = false;
public bool SetupWizardCompleted { get; set; } = false;
/// <summary>Comma-separated step numbers that have been completed (e.g. "1,2,3")</summary>
public string? SetupWizardDoneSteps { get; set; }
/// <summary>Comma-separated step numbers the user chose to skip</summary>
public string? SetupWizardSkippedSteps { get; set; }
/// <summary>UTC timestamp of when the setup wizard was completed. Null if not yet completed.</summary>
public DateTime? SetupWizardCompletedAt { get; set; }
/// <summary>ASP.NET Identity user ID of the user who completed the setup wizard.</summary>
public string? SetupWizardCompletedByUserId { get; set; }
/// <summary>Display name of the user who completed the setup wizard, stored at completion time
/// to avoid a runtime JOIN to the Identity tables when listing companies.</summary>
public string? SetupWizardCompletedByName { get; set; }
/// <summary>True when the company indicated they are migrating data from QuickBooks Desktop.</summary>
public bool MigratingFromQuickBooks { get; set; } = false;
/// <summary>JSON blob persisting QB Migration Wizard step state across sessions.</summary>
public string? QbMigrationStateJson { get; set; }
// Kiosk settings
/// <summary>
/// Controls what the kiosk creates on submission: "Quote" (default) or "Job".
/// Quote aligns with the default Terms text ("subject to a formal quote").
/// Job is for shops that price on the spot and want the work order ready immediately.
/// </summary>
public string KioskIntakeOutput { get; set; } = "Quote";
// Guided activation / first-workflow onboarding
/// <summary>Selected first-workflow path: quote_first or job_first. Null until chosen.</summary>
public string? OnboardingPath { get; set; }
/// <summary>True once the company completes its first guided real workflow.</summary>
public bool FirstWorkflowCompleted { get; set; } = false;
/// <summary>UTC timestamp of when the first guided workflow was completed.</summary>
public DateTime? FirstWorkflowCompletedAt { get; set; }
/// <summary>UTC timestamp of the company's first quote creation.</summary>
public DateTime? FirstQuoteCreatedAt { get; set; }
/// <summary>UTC timestamp of the company's first job creation.</summary>
public DateTime? FirstJobCreatedAt { get; set; }
/// <summary>UTC timestamp of the company's first invoice creation.</summary>
public DateTime? FirstInvoiceCreatedAt { get; set; }
/// <summary>UTC timestamp of when the company dismissed guided activation without completing it.</summary>
public DateTime? GuidedActivationDismissedAt { get; set; }
// Navigation
public virtual Company Company { get; set; } = null!;
}