Themia.ETax.Withholding.Data
0.36.0
dotnet add package Themia.ETax.Withholding.Data --version 0.36.0
NuGet\Install-Package Themia.ETax.Withholding.Data -Version 0.36.0
<PackageReference Include="Themia.ETax.Withholding.Data" Version="0.36.0" />
<PackageVersion Include="Themia.ETax.Withholding.Data" Version="0.36.0" />
<PackageReference Include="Themia.ETax.Withholding.Data" />
paket add Themia.ETax.Withholding.Data --version 0.36.0
#r "nuget: Themia.ETax.Withholding.Data, 0.36.0"
#:package Themia.ETax.Withholding.Data@0.36.0
#addin nuget:?package=Themia.ETax.Withholding.Data&version=0.36.0
#tool nuget:?package=Themia.ETax.Withholding.Data&version=0.36.0
Themia.ETax.Withholding.Data
Framework side of Themia.ETax.Withholding (net10.0, PostgreSQL): the migration helpers for your own withholding certificate table and the
gapless certificate-number source. It is a separate package from Themia.ETax.Data so an app that only issues tax invoices does not pull
the withholding package.
The required guards (WithholdingCertificateGuards)
Call all three from the migration that creates or adopts your table, after the table exists. Every statement is safe to run again.
var map = new WithholdingCertificateTableMap { Table = "withholding_certificates", IssuedStateValue = 1, CancelledStateValue = 2 };
WithholdingCertificateGuards.ApplyImmutability(this, map);
WithholdingCertificateGuards.ApplySamePayerReplacementKey(this, map);
WithholdingCertificateGuards.ApplyIssueKey(this, map);
ApplyImmutabilityreuses the tax document guard SQL (Themia.ETax.Data). AnIssuedcertificate may leaveIssuedonly by being cancelled with a replacement (the rule the tax document guard applies to a sent document), so refusal messages say "tax document" and "sent" for certificates too. Cancelled is final, the replacement is write-once, no delete, no truncate. Rows without a snapshot (legacy rows) are guarded against delete only. The default immutable list follows the id, payer, source event, revision and snapshot columns you configure; a table that names the others differently lists its ownImmutableColumns(which must contain the snapshot column).ApplySamePayerReplacementKeyadds a unique index on (id, payer) and a composite foreign key (replaced-by, payer) to (id, payer), deferred to commit, so a replacement must belong to the replaced certificate's payer.ApplyIssueKeycreatesUNIQUE (source event, revision)as an index namedux_<table>_source_revision(created only if absent), so two certificates can never exist for one idempotency key even if issuers race past the store's lookup. An app that already has an equivalent unique constraint under another name ends up with a second, redundant index and can drop either.- Identifiers are validated, then quoted; the table name is capped at 39 characters so every derived name stays within PostgreSQL's 63.
Other engines: the migration throws
NotSupportedExceptionrather than leave issued certificates editable. - Run the application as a role that does NOT own the table: the trigger stops application bugs, not someone with DDL rights.
Certificate numbers (SequenceWithholdingNumberSource)
services.AddThemiaETaxWithholdingSequenceNumbers() registers the IWithholdingNumberSource: one gapless run per payer and per year of the issue
date over Themia's transactional sequence allocator (key DocNo:ETax:Withholding:{payer}:{yyyy}, separate from every tax document run). The printed
shape, the prefix and the year style are the adopter's options (below); the counter key always uses the Gregorian year, so changing the printed style
never restarts a run. Needs AddThemiaSequences.
Number shape and series
WithholdingNumberOptions.Shape chooses one of four shapes; the running number is always 6 digits, and Year chooses the year style
(Buddhist era by default, Gregorian an option):
Shape |
Prints (issue date in 2026, 10th number) |
|---|---|
PrefixYearSeq (default) |
WHT-2569-000010 (Prefix is WHT unless you set it) |
SeqSlashYear2 |
000010/69 |
PrefixYearMonthSeq |
WHT-6910-000010 (2-digit year of the era, 2-digit month; the only shape that allows Period = Monthly) |
YearSeq |
2569-000010 |
The run restarts yearly by default (Period). Period = Monthly restarts it each month and is valid only with PrefixYearMonthSeq: every other shape
prints the year only, so a monthly run would print the same number in two months, and the options fail at startup instead.
A series is the form's เล่มที่ used as a short code (CR, SF). It is empty by default. Pass Series in the issue request and that series has
its own gapless run per payer and per year of the issue date; it is printed as เล่มที่ (not inside the number). The series is part of the run key
(…:series=CR:2026), so the run of a payer that never uses a series keeps its key. 1 to 20 letters, digits, hyphens or underscores.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Dapper (>= 2.1.79)
- FluentMigrator (>= 8.0.1)
- Handlebars.Net (>= 2.1.6)
- Microsoft.Data.SqlClient (>= 6.1.5)
- Microsoft.Extensions.Caching.Memory (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.19.1)
- Microsoft.IdentityModel.Logging (>= 8.19.1)
- Microsoft.IdentityModel.Protocols (>= 8.19.1)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.19.1)
- Microsoft.IdentityModel.Tokens (>= 8.19.1)
- MySqlConnector (>= 2.6.0)
- Npgsql (>= 10.0.3)
- PuppeteerSharp (>= 25.1.2)
- System.IdentityModel.Tokens.Jwt (>= 8.19.1)
- System.Security.Cryptography.Pkcs (>= 10.0.13)
- Themia.ETax.Data (>= 0.36.0)
- Themia.ETax.Withholding (>= 0.36.0)
- Themia.Framework.Data.Sequences (>= 0.36.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.