Bundle
dsh-customs-doc-check
报关单证一致性核对(按公开的报关单填制规范核对单证间字段一致性,仅提示差异,不作出定性结论)
- Source
- PerryLink
- License
- Apache-2.0
- Updated
- Updated 7 days ago
Readme
# dsh-customs-doc-check — Customs declaration document register consistency check
[](https://dsh.market/)
`dsh-customs-doc-check` reads one declaration supporting-document register — a 报关单证台账 whose own column names may be Chinese or English — and checks that register itself rather than the declaration behind it: that each row carries at least one of the goods name and the amount, that the commodity code is ten digits and the currency a three-letter code, that no document number is repeated, that gross weight is not below net weight, that a declaration date, where one is recorded, parses and is not later than the check date, and that no unreplaced template placeholder survives in the goods-name column.
## What it looks like

Real output from this plugin over its own `CD-001` test fixture — not a mock-up. The rule pack ships no invented quotations, so a finding names both the clause it applied and the fact that the clause text was not obtained.
## What it answers
| You ask | What it answers |
|---|---|
| One row's goods name still reads `待填` and its amount cell is empty — is anything reported? | Not by `CD-001`: it only asks that at least one of the goods name and the amount be filled, and `待填` is not blank. What reports the row is `CD-007`, which looks in the goods-name column for the placeholder terms the pack lists (`【`, `{{`, `XXX`, `待填`, `TBD` and the like). `CD-001` never judges whether the declared content is truthful; `CD-007` only looks for those terms in that one column, and its term list can be adjusted to your own template. |
| The commodity code is written `84713000` — eight digits. Is that reported? | Yes. `CD-002` matches the cell against ten digits and reports anything else. It checks the digit count and the legality of the characters only, never whether the code is the right one: the same goods may legitimately carry different codes under different classification decisions. The digit count is the rule's `pattern` parameter, so it can be changed when the tariff changes its digit count. |
| The currency column reads `RMB` in one row and `usd` in another — which row is reported? | `usd`: `CD-003` accepts three upper-case letters. `RMB` fits that shape and passes, because the rule is a shape check and not the wording of the clause — the code table the clause points to was not obtained, and the rule deliberately takes no position on the `CNY` versus `RMB` spelling that customs practice also uses; tighten the `pattern` if your house style fixes one. It does not judge whether the currency chosen is the right one for the settlement. |
| The same document number appears in two rows — what does the check say? | `CD-004` reports the repeated number; whitespace is ignored when comparing. Uniqueness is all the rule establishes: a hit usually means the same document was registered twice or a number was copied from another document, and which of the two is wrong is for a person to confirm. It does not decide which registration is the valid one. |
| Gross weight reads `1,180` and net weight `1,250` — is that caught? And what if a row has no net weight? | The first is reported: `CD-005` compares the two values and reports a row where gross weight is below net weight. Only the numbers are compared and units are allowed (`12.5KGS`), and the rule never judges whether the declared weight is truthful. A row missing either field is not compared at all; where that leaves nothing to compare, the rule lands in `skipped` with its reason instead of reporting a difference. |
| The register carries no declaration date at all, and one row's date is `2026-13-40`. What is reported? | The impossible date is reported: `CD-006` requires the date to parse and not to be later than the check date. A missing declaration date is not reported as a defect — at declaration time that column is 免予填报, being recorded by the customs computer system rather than by the declarant, so the rule only checks a date that is there. A date that cannot be parsed is reported on its own and never silently skipped, and the rule does not judge whether the declaration was made within a statutory time limit. |
## Standards it follows
| Document | Number | Cited by rules |
|---|---|---|
| 《中华人民共和国海关进出口货物报关单填制规范》 | 海关总署公告 2019 年第 18 号(本次未取得条文) | CD-001, CD-002, CD-005, CD-007 |
| 《表示货币的代码》 | GB/T 12406—2022(表示货币的代码;2022-12-30 发布并实施;全部代替 GB/T 12406—2008(该版名称为「表示货币和资金的代码」)——注意旧版名称含"资金";修改采用 ISO 4217:2015,非等同采用;条号本次未取得) | CD-003 |
| 《中华人民共和国海关进出口货物申报管理规定》 | 海关总署令(现行令号本次未核实) | CD-004, CD-006 |
**Boundary:** this plugin checks a **报关单证台账** for what a register can be held to mechanically — that
the key columns are filled, that the commodity code and currency follow their formats, that document
numbers are unique, that gross weight is not below net weight, that dates parse, and that no template
placeholder survives. It does **not** decide whether a declaration is truthful, whether it amounts to
misdeclaration, whether it affects duty, or whether goods should be inspected or penalised. **Those calls
belong to Customs**, and the same goods may legitimately carry different codes under different
classification decisions. **This plugin never judges classification.**
> ### ⚠️ What the citations rest on
>
> **《中华人民共和国海关进出口货物报关单填制规范》was obtained in full and read article by article** (49 articles
> plus the closing definitions), and `rules/evidence/clause-verification.md` records what was quoted: article 34
> (the commodity code is **10 digits**), articles 5 and 6 (dates are **8 digits**, year-month-day, and — worth
> knowing — the declaration date is 「**在申报时免予填报**」, so a register lacking it is normal), article 39
> (currency is taken from the 《货币代码表》), articles 24–26 (packages, gross and net weight) and article 38.
> The **issuing facts were confirmed against the official announcement**: 《海关总署公告 2019 年第 18 号》,
> dated 2019-01-22, **in force from 2019-02-01, and it repealed 海关总署 2018 年第 60 号公告**, so citing that one
> is simply wrong. 《进(出)境货物备案清单》 is filled **比照** this specification.
>
> **Two claims were corrected as a result.** `CD-003` said currency "should be three letters" — the article says
> only 「按海关规定的《货币代码表》选择相应的货币名称及代码填报」, and that code table **was not obtained**, so
> three letters is an inference about the table's shape, not the article's wording. And `CD-006` now states the
> exemption above rather than implying a missing declaration date is a defect.
>
> **Every `excerpt` still says "本次未取得" and every rule stays `warn` or `info`** — deliberately. The
> specification governs **how the declaration form's columns are filled in**, while this plugin checks an
> enterprise's **supporting-document register**. A register is not a declaration form, so raising a rule to
> `direct` would dress a register gap up as a customs obligation. **《申报管理规定》and the various code tables
> (关区、运输方式、监管方式、货币、国别、港口 etc.) were all unobtained**, which is exactly why no code table is
> built in. The specification text itself was read from a **local trade-promotion council's reprint**, because the
> official attachment is a `.doc` and this tool cannot decode binary Office files.
>
> Two checks deserve a note of their own. The commodity-code check verifies **ten digits and nothing
> else** — the digit count comes from the current tariff, and the check never says whether a code is the
> *right* code. The currency check verifies **three upper-case letters**, and deliberately takes no
> position on `CNY` versus the `RMB` spelling that Customs practice also uses; narrow the pattern if your
> house style is one of them.
## Compatibility
| Surface | Status |
|---|---|
| Harness | Peer range `>=0.1.2-rc.1 <0.2.0 \|\| >=0.2.0-0 <0.3.0` — verified to accept both `0.2.0-rc.2` and `0.2.1-alpha.1`. `engines.dsh` is deliberately not declared: it has no reader and cannot reject a host |
| Node | `^22.19.0 || >=24.0.0` |
| Platforms | All (plain ESM; no native code, no network, no model call) |
| Tool mode | Works in `native`, `ptc` and `both`; for a batch of declarations use `ptc` |
## What it does
Registers the `customs_doc_check` tool. It reads one declaration register — the documents' own column names,
in Chinese or English — applies a versioned rule pack, and returns a report.
| Rule | Check | Severity | Basis kind |
|---|---|---|---|
| `CD-001` | the register records a goods name and an amount | warn | principle |
| `CD-002` | the commodity code is ten digits | warn | principle |
| `CD-003` | the currency is a three-letter code | warn | principle |
| `CD-004` | document numbers are unique in the register | warn | principle |
| `CD-005` | gross weight is not below net weight | warn | principle |
| `CD-006` | the declaration date parses and is not in the future | warn | principle |
| `CD-007` | the goods name holds no unreplaced placeholder | warn | principle |
## Install
```sh
dsh plugin --profile <name> add dsh-customs-doc-check
dsh --profile <name> --dump-config | grep 'dsh-customs-doc-check'
```
## Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| `rulesFile` | string | `rules/customs-doc-check.yaml` | Rule-pack path, relative to the package root |
| `disabledRules` | string[] | `[]` | Rule ids to stop running; each appears in `skipped` |
| `onlyRules` | string[] | `[]` | Run only these rule ids; empty runs every rule |
| `skipNotes` | string | `""` | Note appended to every `skipped` reason |
| `timeoutMs` | number | `120000` | Cooperative tool timeout budget |
Rule-level parameters worth knowing:
- `CD-002` `pattern` — the commodity-code shape; the default demands ten digits. Change it when the tariff
changes its digit count.
- `CD-003` `pattern` — the currency shape; the default demands three upper-case letters. Tighten it to one
spelling of the local currency if your house style fixes one.
- `CD-005` `relation` — the comparison between gross and net weight, `gte` by default.
- `CD-007` `terms` — the placeholders to look for.
## Material format
The tool accepts JSON or YAML:
```yaml
declarationNo: '530120260310000001'
consignee: 某某进出口有限公司
contractNo: SC-2026-018
rows:
- { 单证种类: 发票, 单证编号: INV-2026-0001, 商品编号: '8471300000',
商品名称: 便携式自动数据处理设备, 数量: '100', 总价: '45000.00', 币制: USD,
毛重: '1250.5', 净重: '1180.0', 申报日期: 2026-03-10 }
```
Column names are matched case-insensitively and ignoring spaces, underscores and hyphens, so `商品编号`
and `hsCode` resolve to the same field; the register's own column names are kept, so a finding names the
column it read. Numeric cells may carry units and thousands separators (`12.5KGS`, `1,250.5`).
## Rule sources
Rule data lives in `rules/customs-doc-check.yaml`. The pack's header states the citation gap in full, and
each rule's `note` repeats the part that matters for that rule. The load-time guard that normally enforces
"an excerpt must be a real quotation of at least eight characters" cannot tell a quotation from a
description — so this pack leans on the header, the per-rule notes and a test that asserts every `excerpt`
admits the gap.
## Troubleshooting
- **`CD-002` fires on a code I know is valid.** The cell is not exactly ten digits — it may carry a
trailing space, a dot, or a sub-item suffix. Normalise the export, or change `pattern`.
- **`CD-003` fires on `RMB`.** The default pattern accepts any three letters, so this means the cell is not
three letters — `RMB¥`, `人民币`, or a lower-case code would all fail. Normalise, or narrow the pattern.
- **`CD-006` fires on a date in the future.** That is the check: a declaration dated after the review date
means either the date or the review basis is wrong.
- **The reader refuses a register it used to accept.** It found none of the known column names; the error
names the columns it saw.
- **The plugin installs but the tool never appears.** Check that `main` resolves to `lib/index.mjs` and
that `pnpm run build` produced it; a wrong `main` makes the loader skip the entry silently.
- **`dsh plugin add` refuses the package as incompatible.** The peer range covers `0.1.x` and `0.2.x`; if
your runtime sits outside it, grant an explicit exemption:
`dsh plugin --profile <name> allow-version dsh-customs-doc-check@0.1.0 --dsh-version <runtime> --accept-risk`
- **`check` reports `manifest-peers` as failed.** The static checker compares against a hard-coded peer
range that predates the 0.2 line. The runtime enforces peer compatibility at install time, so the
declared range is the correct one; this is a known upstream issue in `dsh-plugin-dev`.
## Development
```sh
pnpm install
pnpm run typecheck # tsc --noEmit
pnpm test # vitest, the shared table-plugin suite plus paired fixtures
pnpm run build # tsdown -> lib/index.mjs + lib/index.d.mts
node ../scripts/sync-shared.mjs dsh-customs-doc-check # refresh src/shared from ../_shared
```
The plugin is **data-only**: `src/model.ts` declares the table shape, the shared kit supplies the reader and
the check engine, and the rule pack declares every check. A check that fits an existing kind needs a rule
pack edit and nothing else.
## License
[Apache License 2.0](LICENSE) © 2026 dsh-customs-doc-check contributors.
Install
dsh plugin --profile web add github:PerryLink/dsh-customs-doc-check
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-customs-doc-check from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.