Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

All notable changes to this package are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.1] - 2026-05-14

### Fixed

- `buildCreateDocumentPayload` no longer emits `Payments: []` — SUMIT rejected document-create requests that included an empty `Payments` array.
- `Details.Language` is now sent as the numeric enum SUMIT requires (`Accounting_Typed_Language`: Hebrew=0, English=1, Arabic=2, Spanish=3) rather than the literal `"he"`/`"en"` string that returned `Details.Language: Error converting value "he"` from SUMIT.
- `Customer.SearchMode` is now derived automatically: SUMIT id ⇒ `1`, ExternalIdentifier ⇒ `2`, otherwise `0`. Previously every request hardcoded `0`, which prevented customer upserts.
- Empty / whitespace-only optional fields (`emailAddress`, `phone`, `taxId`, item `description`, `sku`, etc.) are now stripped rather than sent as `""` — SUMIT rejects empty strings on several optional fields.

### Added

- `SUMIT_DOCUMENT_TYPE` now exposes the full `Accounting_Typed_DocumentType` enum: `Invoice` (0), `InvoiceAndReceipt` (1), `Receipt` (2), `ProformaInvoice` (3), `PriceQuotation` (12), and all credit/expense variants.
- `SUMIT_LANGUAGE` const exposing `Hebrew=0`, `English=1`, `Arabic=2`, `Spanish=3`.
- `language` / `responseLanguage` params accept the shorthand strings `"he"`/`"en"`/`"ar"`/`"es"` (and full English names) in addition to numeric codes.

### Changed

- `SumitNormalizedEventType` and supporting types unchanged.
- `SUMIT_DOCUMENT_TYPE.TransactionInvoice` is retained as a deprecated alias to `1` for backwards compatibility, but **its meaning was wrong in 0.3.0** — code `1` is `InvoiceAndReceipt` (חשבונית מס-קבלה), not the pre-payment "חשבון עסקה". Use `SUMIT_DOCUMENT_TYPE.ProformaInvoice` (3) for חשבון עסקה.

## [0.3.0] - 2026-05-13

### Added
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ import { buildCreateDocumentPayload, SUMIT_DOCUMENT_TYPE } from "sumit-api";
const payload = buildCreateDocumentPayload({
companyId: 123,
apiKey: process.env.SUMIT_API_KEY!,
documentType: SUMIT_DOCUMENT_TYPE.TransactionInvoice, // 1
documentType: SUMIT_DOCUMENT_TYPE.ProformaInvoice, // 3 — חשבון עסקה
customer: {
externalIdentifier: "client_42",
name: "Acme Ltd",
Expand All @@ -135,7 +135,11 @@ const payload = buildCreateDocumentPayload({
});
```

`SUMIT_DOCUMENT_TYPE` only lists values this package has actively verified. SUMIT exposes many more document type codes — pass any number directly via the `documentType` field.
`SUMIT_DOCUMENT_TYPE` covers SUMIT's `Accounting_Typed_DocumentType` enum — `Invoice` (0, חשבונית מס), `InvoiceAndReceipt` (1, חשבונית מס-קבלה), `Receipt` (2, קבלה), `ProformaInvoice` (3, חשבון עסקה), `PriceQuotation` (12, הצעת מחיר), credit/expense variants, and more. Pass any numeric code directly via `documentType` if needed.

`language` accepts either a `SUMIT_LANGUAGE` numeric code or the shorthand strings `"he"`/`"en"`/`"ar"`/`"es"` (and their full English names) — the helper converts to the numeric enum SUMIT requires. Unknown strings are dropped silently.

`customer.searchMode` is derived automatically when omitted: SUMIT id `1`, ExternalIdentifier `2`, otherwise `0` (create new). Pass an explicit value to override.

---

Expand Down
8 changes: 4 additions & 4 deletions docs/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Issues a SUMIT accounting document (חשבון עסקה / חשבונית מס /
{
Credentials: { CompanyID, APIKey },
Details: {
Type: number, // SUMIT document type code; 1 = חשבון עסקה
Type: number, // SUMIT document type code; 3 = חשבון עסקה (ProformaInvoice)
Customer: {
SearchMode: 0 | 1 | 2, // 0 = match by ID (default for this endpoint)
Name: string,
Expand All @@ -138,7 +138,7 @@ Issues a SUMIT accounting document (חשבון עסקה / חשבונית מס /
NoVAT?: boolean,
},
SendByEmail?: { EmailAddress, Original, SendAsPaymentRequest },
Language?: string, // e.g. "he" / "en"
Language?: number, // Accounting_Typed_Language enum: 0=Hebrew, 1=English, 2=Arabic, 3=Spanish
Currency?: "ILS" | "USD" | "EUR",
Description?: string,
ExternalReference?: string,
Expand All @@ -159,11 +159,11 @@ Issues a SUMIT accounting document (חשבון עסקה / חשבונית מס /
SearchMode: 0 | 1 | 2,
},
}],
Payments: [],
// Payments omitted — SUMIT rejects empty Payments arrays on document creation.
VATIncluded: boolean,
VATPerItem?: boolean,
VATRate?: number,
ResponseLanguage?: string,
ResponseLanguage?: number,
}
```

Expand Down
123 changes: 116 additions & 7 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ describe("@deepclaw/sumit", () => {
const payload = buildCreateDocumentPayload({
companyId: 123,
apiKey: "api-key",
documentType: SUMIT_DOCUMENT_TYPE.TransactionInvoice,
documentType: SUMIT_DOCUMENT_TYPE.ProformaInvoice,
customer: {
externalIdentifier: "client-1",
name: "אקמה בע״מ",
Expand Down Expand Up @@ -378,15 +378,15 @@ describe("@deepclaw/sumit", () => {
expect(payload).toEqual({
Credentials: { CompanyID: 123, APIKey: "api-key" },
Details: {
Type: 1,
Type: 3,
Customer: {
SearchMode: 0,
SearchMode: 2, // derived from externalIdentifier
Name: "אקמה בע״מ",
EmailAddress: "billing@example.invalid",
ExternalIdentifier: "client-1",
CompanyNumber: "514999000",
},
Language: "he",
Language: 0, // Hebrew
Currency: "ILS",
},
Items: [
Expand All @@ -403,16 +403,125 @@ describe("@deepclaw/sumit", () => {
Item: { Name: "שעות פיתוח", SearchMode: 0 },
},
],
Payments: [],
VATIncluded: false,
});
});

it("omits Payments key entirely (SUMIT rejects empty Payments arrays on document creation)", () => {
const payload = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "C" },
items: [{ name: "Item", unitPrice: 10 }],
});
expect("Payments" in payload).toBe(false);
});

it("derives Customer.SearchMode: 0 default, 2 with externalIdentifier, 1 with id", () => {
const a = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "A" },
items: [{ name: "Item", unitPrice: 1 }],
});
expect(a.Details.Customer.SearchMode).toBe(0);

const b = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "B", externalIdentifier: "ext-1" },
items: [{ name: "Item", unitPrice: 1 }],
});
expect(b.Details.Customer.SearchMode).toBe(2);

const c = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "C", id: "sumit-cust-1" },
items: [{ name: "Item", unitPrice: 1 }],
});
expect(c.Details.Customer.SearchMode).toBe(1);

const d = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "D", externalIdentifier: "ext-1", searchMode: 0 },
items: [{ name: "Item", unitPrice: 1 }],
});
expect(d.Details.Customer.SearchMode).toBe(0); // explicit caller value wins
});

it("converts language strings to SUMIT numeric enum and drops unknown values", () => {
const he = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "C" },
items: [{ name: "I", unitPrice: 1 }],
language: "he",
});
expect(he.Details.Language).toBe(0);

const en = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "C" },
items: [{ name: "I", unitPrice: 1 }],
language: "English",
});
expect(en.Details.Language).toBe(1);

const numeric = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "C" },
items: [{ name: "I", unitPrice: 1 }],
language: 2,
});
expect(numeric.Details.Language).toBe(2);

const unknown = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: { name: "C" },
items: [{ name: "I", unitPrice: 1 }],
language: "klingon",
});
expect(unknown.Details.Language).toBeUndefined();
});

it("trims whitespace and drops empty strings from customer/item optional fields", () => {
const payload = buildCreateDocumentPayload({
companyId: 1,
apiKey: "k",
documentType: 3,
customer: {
name: "C",
emailAddress: " ",
phone: "",
taxId: " 514999000 ",
},
items: [{ name: "I", description: "", unitPrice: 10 }],
});
expect(payload.Details.Customer.EmailAddress).toBeUndefined();
expect(payload.Details.Customer.Phone).toBeUndefined();
expect(payload.Details.Customer.CompanyNumber).toBe("514999000");
expect(payload.Items[0].Item.Description).toBeUndefined();
});

it("includes SendByEmail when requested and maps currency strings", () => {
const payload = buildCreateDocumentPayload({
companyId: 7,
apiKey: "k",
documentType: 1,
documentType: 3,
customer: { name: "C" },
items: [{ name: "Item", unitPrice: 10 }],
currency: "USD",
Expand Down Expand Up @@ -476,7 +585,7 @@ describe("@deepclaw/sumit", () => {
const payload = buildCreateDocumentPayload({
companyId: 1,
apiKey: "super-secret",
documentType: 1,
documentType: 3,
customer: { name: "C", emailAddress: "c@example.invalid" },
items: [{ name: "Item", unitPrice: 10 }],
});
Expand Down
Loading
Loading