Add company default GL accounts; move QB sign-fix to platform

Default accounts: companies can now set a default Revenue, COGS, and
Inventory account (Chart of Accounts -> "Default Accounts" card). Stored
on CompanyPreferences (3 nullable FKs + migration). Used as the fallback
when an item or invoice line leaves its account blank:
 - Invoice lines fall back to the default Revenue account, then to 4000.
 - New inventory/catalog items are pre-filled with the COGS/Inventory
   defaults, so the value is stored on the item and both live posting and
   the balance-recompute path stay consistent.
Blank defaults = unchanged behavior, so nothing changes until a company
opts in. Setting both COGS + Inventory enables perpetual-inventory COGS
posting (warned in the UI and help docs). Help KB + Settings article
updated.

Also moves the "Fix QB Import Signs" tool off the company Chart of
Accounts page (was CompanyAdmin-visible) to the SuperAdmin-only platform
Company Details page, operating on the target company.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 10:03:11 -04:00
parent 687aedf7a4
commit ee86d7aaf6
14 changed files with 11913 additions and 44 deletions
@@ -2185,6 +2185,9 @@ namespace PowderCoating.Infrastructure.Migrations
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<int?>("DefaultCogsAccountId")
.HasColumnType("int");
b.Property<string>("DefaultCurrency")
.IsRequired()
.HasColumnType("nvarchar(max)");
@@ -2193,6 +2196,9 @@ namespace PowderCoating.Infrastructure.Migrations
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int?>("DefaultInventoryAccountId")
.HasColumnType("int");
b.Property<string>("DefaultJobPriority")
.IsRequired()
.HasColumnType("nvarchar(max)");
@@ -2204,6 +2210,9 @@ namespace PowderCoating.Infrastructure.Migrations
b.Property<int>("DefaultQuoteValidityDays")
.HasColumnType("int");
b.Property<int?>("DefaultRevenueAccountId")
.HasColumnType("int");
b.Property<string>("DefaultTimeFormat")
.IsRequired()
.HasColumnType("nvarchar(max)");
@@ -2380,6 +2389,12 @@ namespace PowderCoating.Infrastructure.Migrations
b.HasIndex("CompanyId")
.IsUnique();
b.HasIndex("DefaultCogsAccountId");
b.HasIndex("DefaultInventoryAccountId");
b.HasIndex("DefaultRevenueAccountId");
b.ToTable("CompanyPreferences");
});
@@ -7241,7 +7256,7 @@ namespace PowderCoating.Infrastructure.Migrations
{
Id = 1,
CompanyId = 0,
CreatedAt = new DateTime(2026, 6, 20, 0, 29, 46, 909, DateTimeKind.Utc).AddTicks(3976),
CreatedAt = new DateTime(2026, 6, 20, 13, 49, 14, 564, DateTimeKind.Utc).AddTicks(4507),
Description = "Standard pricing for regular customers",
DiscountPercent = 0m,
IsActive = true,
@@ -7252,7 +7267,7 @@ namespace PowderCoating.Infrastructure.Migrations
{
Id = 2,
CompanyId = 0,
CreatedAt = new DateTime(2026, 6, 20, 0, 29, 46, 909, DateTimeKind.Utc).AddTicks(3981),
CreatedAt = new DateTime(2026, 6, 20, 13, 49, 14, 564, DateTimeKind.Utc).AddTicks(4514),
Description = "5% discount for preferred customers",
DiscountPercent = 5m,
IsActive = true,
@@ -7263,7 +7278,7 @@ namespace PowderCoating.Infrastructure.Migrations
{
Id = 3,
CompanyId = 0,
CreatedAt = new DateTime(2026, 6, 20, 0, 29, 46, 909, DateTimeKind.Utc).AddTicks(3982),
CreatedAt = new DateTime(2026, 6, 20, 13, 49, 14, 564, DateTimeKind.Utc).AddTicks(4515),
Description = "10% discount for premium customers",
DiscountPercent = 10m,
IsActive = true,
@@ -9580,6 +9595,21 @@ namespace PowderCoating.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PowderCoating.Core.Entities.Account", null)
.WithMany()
.HasForeignKey("DefaultCogsAccountId")
.OnDelete(DeleteBehavior.NoAction);
b.HasOne("PowderCoating.Core.Entities.Account", null)
.WithMany()
.HasForeignKey("DefaultInventoryAccountId")
.OnDelete(DeleteBehavior.NoAction);
b.HasOne("PowderCoating.Core.Entities.Account", null)
.WithMany()
.HasForeignKey("DefaultRevenueAccountId")
.OnDelete(DeleteBehavior.NoAction);
b.Navigation("Company");
});