eims integration master test complete

This commit is contained in:
Hagernesh
2026-08-12 14:08:28 +00:00
parent 8d53dc17ce
commit 2d4da8110b
38 changed files with 2270 additions and 173 deletions

View File

@@ -0,0 +1,68 @@
import { BaseEntity } from "@edr/api-common";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
import { Invoice } from "../../billing/entities/invoice.entity";
import { EimsInvoiceError } from "../eims-registration.types";
export type EimsReceiptKind = "SALES" | "WITHHOLDING";
/** Same shape as `EimsInvoiceStatus`, for a receipt instead of an invoice. */
export enum EimsReceiptStatus {
NotSubmitted = "NOT_SUBMITTED",
Submitting = "SUBMITTING",
Registered = "REGISTERED",
Failed = "FAILED",
Unknown = "UNKNOWN",
}
/**
* One `POST /v1/receipt/sales` or `POST /v1/receipt/withholding` attempt, linked to the invoice it
* was filed against. An invoice can have more than one receipt (partial payments), so this is its
* own table rather than columns on `Invoice`.
*
* No counter/sequence reservation the way invoice registration has: the collection's `/v1/receipt/*`
* examples show no MoR-enforced ordering rule on `ReceiptCounter` (unlike `InvoiceCounter`'s
* documented "expected: N"), so this is a plain row per attempt.
*/
@Entity({ schema: "freight", name: "eims_receipts" })
@Index(["invoiceId"])
export class EimsReceipt extends BaseEntity {
@Column({ name: "invoice_id", type: "uuid" })
invoiceId!: string;
@ManyToOne(() => Invoice)
@JoinColumn({ name: "invoice_id" })
invoice?: Invoice;
@Column({ name: "kind", type: "varchar", length: 16 })
kind!: EimsReceiptKind;
@Column({ name: "status", type: "varchar", length: 20, default: "NOT_SUBMITTED" })
status!: EimsReceiptStatus;
/** Our own generated `ReceiptNumber` / `ManualReceiptNumber` (both sent equal — see the service). */
@Column({ name: "receipt_number", type: "varchar", length: 64 })
receiptNumber!: string;
/** MoR's Receipt Reference Number, returned only on success. */
@Column({ name: "rrn", type: "text", nullable: true })
rrn?: string | null;
/** Base64 PNG from MoR, same convention as `Invoice.eimsSignedQr` — embed directly. */
@Column({ name: "qr", type: "text", nullable: true })
qr?: string | null;
/** MoR's own `"A"` (active) / `"F"` (failed) status marker, stored verbatim. */
@Column({ name: "ack_status", type: "varchar", length: 8, nullable: true })
ackStatus?: string | null;
@Column({ name: "submitted_at", type: "timestamptz", nullable: true })
submittedAt?: Date | null;
@Column({ name: "last_error", type: "jsonb", nullable: true })
lastError?: EimsInvoiceError | null;
/** The exact request body sent — receipts are bearer-only/unsigned, so nothing secret is in it. */
@Column({ name: "request", type: "jsonb", nullable: true })
request?: Record<string, unknown> | null;
}

View File

@@ -30,8 +30,11 @@ export class EimsSystemState extends BaseEntity {
@Column({ name: "in_flight_document_number", type: "bigint", nullable: true })
inFlightDocumentNumber?: number | null;
/** IRN of the last successful registration; null until the first one succeeds. */
@Column({ name: "previous_irn", type: "varchar", length: 64, nullable: true })
/**
* IRN of the last successful registration; null until the first one succeeds. `text`, not a
* fixed varchar — same reasoning as `Invoice.eimsIrn`: a real IRN already overflowed varchar(64).
*/
@Column({ name: "previous_irn", type: "text", nullable: true })
previousIrn?: string | null;
/**