253 lines
7.5 KiB
C#
253 lines
7.5 KiB
C#
using System.Net.Mail;
|
|
using TakeoutSaaS.Domain.Tenants.Enums;
|
|
using TakeoutSaaS.Shared.Abstractions.Constants;
|
|
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
|
|
|
namespace TakeoutSaaS.Application.App.Finance.Invoice;
|
|
|
|
/// <summary>
|
|
/// 发票模块映射与参数标准化。
|
|
/// </summary>
|
|
internal static class FinanceInvoiceMapping
|
|
{
|
|
public static TenantInvoiceType ParseInvoiceTypeRequired(string? value)
|
|
{
|
|
return ParseInvoiceTypeOptional(value)
|
|
?? throw new BusinessException(ErrorCodes.BadRequest, "invoiceType 参数不合法");
|
|
}
|
|
|
|
public static TenantInvoiceType? ParseInvoiceTypeOptional(string? value)
|
|
{
|
|
var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
|
|
if (string.IsNullOrWhiteSpace(normalized))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return normalized switch
|
|
{
|
|
"normal" => TenantInvoiceType.Normal,
|
|
"special" => TenantInvoiceType.Special,
|
|
_ => throw new BusinessException(ErrorCodes.BadRequest, "invoiceType 参数不合法")
|
|
};
|
|
}
|
|
|
|
public static TenantInvoiceStatus? ParseStatusOptional(string? value)
|
|
{
|
|
var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
|
|
if (string.IsNullOrWhiteSpace(normalized))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return normalized switch
|
|
{
|
|
"pending" => TenantInvoiceStatus.Pending,
|
|
"issued" => TenantInvoiceStatus.Issued,
|
|
"voided" => TenantInvoiceStatus.Voided,
|
|
_ => throw new BusinessException(ErrorCodes.BadRequest, "status 参数不合法")
|
|
};
|
|
}
|
|
|
|
public static string ToInvoiceTypeText(TenantInvoiceType value)
|
|
{
|
|
return value switch
|
|
{
|
|
TenantInvoiceType.Normal => "normal",
|
|
TenantInvoiceType.Special => "special",
|
|
_ => "normal"
|
|
};
|
|
}
|
|
|
|
public static string ToInvoiceTypeDisplayText(TenantInvoiceType value)
|
|
{
|
|
return value switch
|
|
{
|
|
TenantInvoiceType.Normal => "普票",
|
|
TenantInvoiceType.Special => "专票",
|
|
_ => "普票"
|
|
};
|
|
}
|
|
|
|
public static string ToStatusText(TenantInvoiceStatus value)
|
|
{
|
|
return value switch
|
|
{
|
|
TenantInvoiceStatus.Pending => "pending",
|
|
TenantInvoiceStatus.Issued => "issued",
|
|
TenantInvoiceStatus.Voided => "voided",
|
|
_ => "pending"
|
|
};
|
|
}
|
|
|
|
public static string ToStatusDisplayText(TenantInvoiceStatus value)
|
|
{
|
|
return value switch
|
|
{
|
|
TenantInvoiceStatus.Pending => "待开票",
|
|
TenantInvoiceStatus.Issued => "已开票",
|
|
TenantInvoiceStatus.Voided => "已作废",
|
|
_ => "待开票"
|
|
};
|
|
}
|
|
|
|
public static string NormalizeCompanyName(string? value)
|
|
{
|
|
return NormalizeRequiredText(value, "companyName", 128);
|
|
}
|
|
|
|
public static string NormalizeApplicantName(string? value)
|
|
{
|
|
return NormalizeRequiredText(value, "applicantName", 64);
|
|
}
|
|
|
|
public static string NormalizeOrderNo(string? value)
|
|
{
|
|
return NormalizeRequiredText(value, "orderNo", 32);
|
|
}
|
|
|
|
public static string NormalizeTaxpayerNumber(string? value)
|
|
{
|
|
return NormalizeRequiredText(value, "taxpayerNumber", 64);
|
|
}
|
|
|
|
public static string? NormalizeOptionalTaxpayerNumber(string? value)
|
|
{
|
|
return NormalizeOptionalText(value, "taxpayerNumber", 64);
|
|
}
|
|
|
|
public static string? NormalizeOptionalKeyword(string? value)
|
|
{
|
|
return NormalizeOptionalText(value, "keyword", 64);
|
|
}
|
|
|
|
public static string? NormalizeOptionalEmail(string? value)
|
|
{
|
|
var normalized = NormalizeOptionalText(value, "contactEmail", 128);
|
|
if (normalized is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
_ = new MailAddress(normalized);
|
|
return normalized;
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, "contactEmail 参数不合法");
|
|
}
|
|
}
|
|
|
|
public static string? NormalizeOptionalPhone(string? value)
|
|
{
|
|
return NormalizeOptionalText(value, "contactPhone", 32);
|
|
}
|
|
|
|
public static string? NormalizeOptionalRemark(string? value, string fieldName, int maxLength = 256)
|
|
{
|
|
return NormalizeOptionalText(value, fieldName, maxLength);
|
|
}
|
|
|
|
public static string NormalizeVoidReason(string? value)
|
|
{
|
|
return NormalizeRequiredText(value, "voidReason", 256);
|
|
}
|
|
|
|
public static decimal NormalizeAmount(decimal value)
|
|
{
|
|
if (value <= 0)
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, "amount 参数不合法");
|
|
}
|
|
|
|
return decimal.Round(value, 2, MidpointRounding.AwayFromZero);
|
|
}
|
|
|
|
public static decimal NormalizeAutoIssueMaxAmount(decimal value)
|
|
{
|
|
if (value <= 0)
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, "autoIssueMaxAmount 参数不合法");
|
|
}
|
|
|
|
return decimal.Round(value, 2, MidpointRounding.AwayFromZero);
|
|
}
|
|
|
|
public static (DateTime? StartUtc, DateTime? EndUtc) NormalizeDateRange(DateTime? startUtc, DateTime? endUtc)
|
|
{
|
|
DateTime? normalizedStart = null;
|
|
DateTime? normalizedEnd = null;
|
|
|
|
if (startUtc.HasValue)
|
|
{
|
|
var utcValue = NormalizeUtc(startUtc.Value);
|
|
normalizedStart = new DateTime(utcValue.Year, utcValue.Month, utcValue.Day, 0, 0, 0, DateTimeKind.Utc);
|
|
}
|
|
|
|
if (endUtc.HasValue)
|
|
{
|
|
var utcValue = NormalizeUtc(endUtc.Value);
|
|
normalizedEnd = new DateTime(utcValue.Year, utcValue.Month, utcValue.Day, 0, 0, 0, DateTimeKind.Utc)
|
|
.AddDays(1)
|
|
.AddTicks(-1);
|
|
}
|
|
|
|
if (normalizedStart.HasValue && normalizedEnd.HasValue && normalizedStart > normalizedEnd)
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, "开始日期不能晚于结束日期");
|
|
}
|
|
|
|
return (normalizedStart, normalizedEnd);
|
|
}
|
|
|
|
public static DateTime NormalizeUtc(DateTime value)
|
|
{
|
|
return value.Kind switch
|
|
{
|
|
DateTimeKind.Utc => value,
|
|
DateTimeKind.Local => value.ToUniversalTime(),
|
|
_ => DateTime.SpecifyKind(value, DateTimeKind.Utc)
|
|
};
|
|
}
|
|
|
|
public static string BuildInvoiceNo(DateTime nowUtc)
|
|
{
|
|
var utcNow = NormalizeUtc(nowUtc);
|
|
return $"INV{utcNow:yyyyMMddHHmmssfff}{Random.Shared.Next(100, 999)}";
|
|
}
|
|
|
|
private static string NormalizeRequiredText(string? value, string fieldName, int maxLength)
|
|
{
|
|
var normalized = (value ?? string.Empty).Trim();
|
|
if (string.IsNullOrWhiteSpace(normalized))
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 参数不合法");
|
|
}
|
|
|
|
if (normalized.Length > maxLength)
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 长度不能超过 {maxLength}");
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
private static string? NormalizeOptionalText(string? value, string fieldName, int maxLength)
|
|
{
|
|
var normalized = (value ?? string.Empty).Trim();
|
|
if (normalized.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (normalized.Length > maxLength)
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 长度不能超过 {maxLength}");
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
}
|