Nordlet

← 文档 / 指南

从其他系统迁移

通过一次调用导入公司的账目——科目表、往来单位、期初余额或完整历史记录、未结发票、固定资产和库存。

当公司从其他会计程序迁移到 Nordlet 时,其账目并不是从零开始的。历史账目导入程序会获取旧系统留下的所有数据,并通过一次调用、一个数据库事务写入系统:如果其中某一行未通过检查,则不会存储任何数据。同一个数据包可以先通过仅检查的端点(check-only endpoint),该端点会执行所有验证并进行回滚。

POST /v1/migration/books/validate   # 检查所有内容,不写入数据
POST /v1/migration/books/import     # 写入所有内容,或者不写入任何内容

Web 应用程序在**账户(Account)→ 迁移(Migration)**下也有同样的两个按钮(粘贴 JSON 或上传文件)。API 密钥需要 ledger:write 权限,以及所包含各个模块的写入权限范围(partners:write、catalog:write、sales:write、purchases:write、assets:write、inventory:write)。这两个端点都接受最大 32 MB 的请求体。

启用日期

选择启用日期(cut-over date):即在 Nordlet 中开始运营公司的第一天。此日期之前的所有内容都保留在旧系统中(或作为历史记录导入,见下文);从该天开始的所有内容都通过正常的端点在 Nordlet 中录入。数据包中的所有金额均为“截至启用日期前一天”的余额:科目收盘试算平衡表、尚未支付的发票、货架上的库存、资产台账中的资产。

在导入之前,请在 Nordlet 中执行以下操作:

  1. 创建公司并应用科目表模板(ledger/accounts/apply-template),或者在数据包的 accounts 部分中列出您需要的科目。
  2. 如果要导入库存,请至少创建一个仓库(inventory/warehouses/create)。
  3. 通过 migration/books/validate 运行数据包,直到它报告没有错误并且您接受其警告为止。

数据包

每个部分都是可选的;请包含您拥有的部分。各部分之间的所有引用均通过代码进行(科目代码、往来单位代码、货品代码、资产类别代码、仓库代码),代码既可以引用 Nordlet 中已有的记录,也可以引用同一数据包中较早部分创建的记录。

{
  "cutoverDate": "2026-01-01",
  "source": "Rivilė GAMA",
  "accounts": [
    { "code": "6210", "name": "Advertising expenses", "type": "expense", "parentCode": "6" }
  ],
  "partners": [
    { "code": "301111222", "name": "UAB Klientas", "vatCode": "LT100001112223", "isCustomer": true },
    { "code": "302222333", "name": "UAB Tiekėjas", "isCustomer": false, "isSupplier": true }
  ],
  "items": [
    { "code": "SKU-1", "name": "Widget", "type": "product", "unit": "vnt", "vatRatePercent": "21" }
  ],
  "assetGroups": [
    { "code": "TRANSP", "name": "Vehicles", "assetAccountCode": "1230", "depreciationAccountCode": "1239",
      "defaultUsefulLifeMonths": 60 }
  ],
  "openingBalances": {
    "balancingAccountCode": "3410",
    "entries": [
      { "accountCode": "2710", "debit": "5000.00" },
      { "accountCode": "2410", "debit": "1500.00" },
      { "accountCode": "2040", "debit": "300.00" },
      { "accountCode": "1230", "debit": "12000.00" },
      { "accountCode": "1239", "credit": "2400.00" },
      { "accountCode": "4430", "credit": "700.00" },
      "credit": "10000.00" }
    ]
  },
  "openReceivables": [
    { "partnerCode": "301111222", "number": "SF-1050", "issueDate": "2025-12-10", "dueDate": "2026-01-09",
      "grossTotal": "1210.00", "vatTotal": "210.00" },
    { "partnerCode": "301111222", "number": "SF-1049", "issueDate": "2025-11-20",
      "grossTotal": "500.00", "outstanding": "290.00" }
  ],
  "openPayables": [
    { "partnerCode": "302222333", "documentNumber": "T-77", "documentDate": "2025-12-15", "grossTotal": "700.00" }
  ],
  "fixedAssets": [
    { "groupCode": "TRANSP", "code": "CAR-1", "name": "Delivery van", "acquisitionDate": "2025-01-01",
      "acquisitionCost": "12000.00", "accumulatedDepreciation": "2400.00", "depreciatedMonths": 12 }
  ],
  "stock": [
    { "itemCode": "SKU-1", "quantity": "30", "unitCost": "10" }
  ]
}

金额是十进制字符串,与整个 API 中的格式一致。source 是一个自由标签,最终会进入期初余额事务的描述中以及迁移发票的备注中。

accounts

科目表行:code(代码)、name(名称)、type(类型,包括 asset、liability、equity、income、expense)、可选的 parentCode(父级代码)和 isPostable(可过账)。已存在的代码将保持不变,并计为 existing(已存在)。当旧系统的科目表与模板不同时使用此功能,或者跳过此项并首先应用模板。

partners 和 items

客户、供应商和目录货品,以 code 为键。在立陶宛,往来单位代码通常是公司注册代码;对于个人,可使用公司内部唯一的任何代码。现有的代码将被跳过,不会更新。货品接受 type(product 或 service)、unit(单位)、barcode(条形码)、vatRatePercent(增值税率)、salePriceExclVat(不含税销售价)、purchasePriceExclVat(不含税采购价)。

openingBalances

旧系统的期末试算平衡表,每个科目一行:accountCode 带有 debit(借方)或 credit(贷方)。这些行将作为一个日记账事务过账,日期为 openingBalances.date(默认为启用日期),单据类型为 migration_opening。各行必须引用可过账的科目。

该事务必须平衡。如果旧系统的试算平衡表不平衡(由于四舍五入、从未结转至权益的利润或丢失的科目),请提供 balancingAccountCode(通常为留存收益,在立陶宛模板中为 3410),差额将过账到该科目;响应会在 balancingAmount 中报告该金额。

每个公司只能存在一个期初余额事务。包含 openingBalances 的第二次导入将被拒绝(返回 409)。事后更正是普通的手动日记账事务(ledger/journal/transactions/create);已过账的事务是不可变的,因此原始事务将保留为导入时的状态。

journal - 完整历史记录(可替代或附加于余额)

如果您希望将旧系统的事务导入 Nordlet(以便早期期间的报告能够正常工作),请在此处发送:每个事务一个对象,包含 date(日期)、可选的 description(描述)和 reference(参考编号),以及包含 accountCode + debit/credit 的 entries(分录)。每个事务必须自身平衡。它们的过账单据类型为 migration。

日期必须在启用日期之前。当数据包还包含 openingBalances 时,历史记录的日期必须在 openingBalances.date 当天或之后 —— 常见的模式是上一财政年度初的余额加上该年度的事务,并将启用日期定为当前年度的第一天。请勿发送同一期间的余额和历史记录:因为余额会被重复计算。

期初余额或历史记录所涉及的每个月份都必须是开放的会计期间(否则返回 409)。

openReceivables 和 openPayables

在启用时仍未支付(全部或部分)的发票。它们将作为已开具的销售发票和已登记的采购发票创建,其中 paidAmount = grossTotal − outstanding,因此银行匹配(bank/transactions/match、bank/transactions/suggest-matches)、债务账龄报告和付款 Webhook 可以像处理任何其他发票一样处理它们。

  • outstanding 默认等于 grossTotal;vatTotal 默认等于 0(净额会自动推导)。
  • dueDate 默认等于单据日期。
  • currency 默认等于公司的发票币种。对于公司本位币以外的任何币种,都需要 fxRate(汇率)—— 即每 1 单位本位币所对应的该币种单位数,与其余 API 保持相同的约定 —— 并且该汇率会存储在发票上,以便后续付款能够正确计算已实现的汇兑差额。
  • 销售发票的 number 是旧系统的完整编号,在公司内必须是唯一的。采购发票在每个往来单位和 documentNumber(单据编号)的组合下必须是唯一的。
  • 这些发票不会再次过账:它们的应收/应付余额已经是期初余额的一部分。它们链接到期初余额事务,这就是为什么 openPayables 要求在同一个数据包中包含 openingBalances。
  • 不会导入发票明细行;发票仅包含总计。旧系统仍然是明细行数据以及启用前期间增值税申报表的来源。

如果旧系统在没有未结明细的情况下跟踪客户余额,请为每个往来单位的余额创建一个发票(例如编号为 OB-301111222),以便进行清算。

编号将继续递增。 当导入的销售发票编号格式为 PREFIX-N 且其开票日期落在启用年度内时,该前缀和年份的编号序列将设置为 N + 1(绝不调低)。响应中会列出生成的 numberSeries;对于其他情况,请使用 reference/series/create 自行设置序列({ documentType: "sale_invoice", prefix, year, startAt })。

assetGroups 和 fixedAssets

固定资产连同其截至目前的折旧一同导入:acquisitionCost(购置成本)、salvageValue(残值)、accumulatedDepreciation(累计折旧)、depreciatedMonths(已折旧月数)和 usefulLifeMonths(使用寿命月数,或使用类别的默认值)。直线折旧法运行(assets/depreciation/post)将根据剩余价值和剩余月数继续进行。累计折旧已等于可折旧价值的资产将创建为 fully_depreciated(已提足折旧)。不会过账任何内容 —— 成本和累计折旧是期初余额的一部分。

类别以 code 为键,并且需要 assetAccountCode(资产科目代码)和 depreciationAccountCode(折旧科目代码,默认费用科目为 6206)。

stock

每个货品的现有库存:itemCode(货品代码)、quantity(数量)、unitCost(单位成本,以 EUR 为单位的单位成本),以及可选的 warehouseCode(仓库代码,默认值:默认仓库)、针对批次或序列号跟踪货品的 lotNumber(批次号)和 expiryDate(到期日)。每一行都变成一个库存入库移动,日期为启用日期,单据类型为 migration,并以此成本启动一个先进先出(FIFO)层。不会过账任何内容 —— 库存价值是期初余额的一部分。

检查与警告

如果缺少科目、存在不可过账的科目、事务不平衡、存在未知的往来单位/货品/类别/仓库代码、数据包内部存在重复的代码或编号、未结金额超过总额、累计折旧超过可折旧价值以及缺少汇率,验证端点将失败(返回 422)。Nordlet 中已存在的编号或代码将返回 409。

除此之外,两个端点都会将明细账与同一数据包中的总账分录进行比较,并将差异作为**warnings**(警告)而非错误返回:

  • openReceivables 的总未结金额与应收账款科目上的余额(sales.receivable 过账规则,默认情况下为 2410)进行比较;
  • openPayables 的总未结金额与应付账款科目(purchases.payables,4430)进行比较;
  • stock 的总成本与库存科目(inventory.stock,2040)进行比较;
  • 按资产类别,将资产的成本与类别的资产科目进行比较,将其累计折旧与折旧科目进行比较。

警告意味着旧系统的明细账与总分类账不一致。在导入之前,请修补数据包(或明知差异并予以接受)。

响应

{
  "dryRun": false,
  "cutoverDate": "2026-01-01",
  "accounts": { "created": 1, "existing": 0 },
  "partners": { "created": 2, "existing": 0 },
  "items": { "created": 1, "existing": 0 },
  "assetGroups": { "created": 1, "existing": 0 },
  "openingBalances": { "journalTransactionId": "…", "date": "2026-01-01", "entries": 8,
                        "debitTotal": "18800.00", "creditTotal": "18800.00", "balancingAmount": "5700.00" },
  "journal": { "transactions": 0, "entries": 0 },
  "openReceivables": { "created": 2, "outstandingTotal": "1500.00" },
  "openPayables": { "created": 1, "outstandingTotal": "700.00" },
  "fixedAssets": { "created": 1, "costTotal": "12000.00", "accumulatedDepreciationTotal": "2400.00" },
  "stock": { "movements": 1, "costTotal": "300.00" },
  "numberSeries": [],
  "warnings": []
}

dryRun: true 标记试运行;此时 journalTransactionId 为 null。导入操作将作为带有相同摘要的 migration.imported 记录在审计日志中。

导入之后

  • 锁定启用日期之前的期间(ledger/periods/lock),以免意外将任何内容过账到已迁移的历史记录中。
  • 拿截至启用日期的试算平衡表和资产负债表与旧系统的期末报告进行核对。
  • 继续正常的流程:开具发票、导入银行对账单、匹配付款 —— 迁移的未结发票将像任何其他发票一样进行结算。

导入程序不涵盖的内容:过往期间的发票明细行和增值税申报表(请将它们保留在旧系统或其归档中)、薪资历史记录以及除发票以外的单据(订单、运单、合同)。