8. Reference Data De-gating
8.1 What changed
Sixteen ApplicationDbSeeder (Shumoul.Infrastructure/Persistence/Initialization/ApplicationDbSeeder.cs)
seed steps were removed from the one-time ExecuteSeedIfNeededAsync/SeedsHistory gate and are now
called directly, unconditionally, on every SeedDatabaseAsync invocation:
| Method | Matching key (already existing, unchanged) |
|---|---|
SeedCurrenciesAsync | Name == entity.Name && CurrencyCode == entity.CurrencyCode |
SeedChartofAccountsAsync | Code |
SeedPaymentMethodsAsync | Name == entity.Name && PaymentType == entity.PaymentType |
SeedLookupGroupsAsync | Code |
SeedAddressTypesAsync | FName |
SeedDeviceTypesAsync | DeviceTypeCode |
SeedDeviceModelsAsync | Code |
SeedBarcodeLabelFieldsAsync | Field |
SeedBarcodeLabelTemplatesAsync | Name (see §8.2 — this check was buggy) |
SeedBarcodeLabelTemplateFieldsAsync | (BarcodeLabelTemplateId, BarcodeLabelFieldId) |
SeedMonthsAsync | No |
SeedZatcaCreditDebitNoteReasonCodesAsync | Code |
SeedZatcaDocumentTypeCodesAsync | Code |
SeedZatcaIdentityTypeCodesAsync | Code |
SeedZatcaInvoiceCategoriesAsync | Code |
SeedZatcaTaxCategoriesAsync | Code |
SeedZatcaTaxCategoryReasonsAsync | Code |
Every one of these already had a real, correct per-row matching key before this phase — de-gating them
only required removing the wrapper; no seeding logic itself needed to change (aside from the one bug
below). This means: new rows added to any of these JSON files later reach every tenant on the next
startup/re-provisioning, instead of being silently stuck behind a SeedsHistory row created on the very
first deploy.
Not de-gated (deliberately unchanged, still one-time-gated): SeedBranchesAsync, SeedWarehousesAsync,
SeedDeviceAsync — see Chapter 3 §3.3.
8.2 Bug found and fixed: SeedBarcodeLabelTemplatesAsync's existence check
The pre-existing check was:
if (dbContext.BarcodeLabelFields.FirstOrDefault(x => x.Name == barcodeLabelTemplate.Name) is null)
This queries BarcodeLabelFields (a different table entirely) instead of BarcodeLabelTemplates — since
a BarcodeLabelField's Name never coincides with a template's Name, this check always returned "not
found," meaning a re-run of this seeder (had it ever been re-invoked) would have duplicated every
template on every call. Under the old one-time gate this was latent and harmless (the method only ever
ran once); de-gating it would have exposed the bug immediately (a growing set of duplicate templates
on every tenant DB re-provisioning), so it was fixed as a required part of this phase:
if (dbContext.BarcodeLabelTemplates.FirstOrDefault(x => x.Name == barcodeLabelTemplate.Name) is null)
8.3 Live confirmation
During manual verification (see Chapter 9), the de-gated steps were
observed running across every already-provisioned tenant's database at host startup (Hangfire re-runs
ApplicationDbSeeder.SeedDatabaseAsync for every active tenant on process start) — consistently reporting
Inserted=0 on tenants that already had every row, confirming both idempotency and that no duplicates
were produced.
