@blob-solutions/vcr-am-sdk 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to `@blob-solutions/vcr-am-sdk`. The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project follows [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [0.18.0] — 2026-08-28
6
+
7
+ Stops making integrators guess a department id.
8
+
9
+ ### Changed
10
+
11
+ - **`SaleItem.department` is now optional.** Omit it and the line inherits the department of the offer it references — for an inline new offer, the `defaultDepartment` you declared on it. Passing one explicitly still works and still wins, which is what you want when you deliberately sell the same SKU out of a second department.
12
+
13
+ The field was required, so every integration had to name a department per line, and the natural thing to write is the first id. That is not a harmless default: departments carry the tax regime, every register is created with one department per regime in a fixed order, and `{ id: 1 }` is the VAT one on every register whether or not the merchant owes VAT. Nothing rejects the mismatch — the receipt simply prints a VAT line the merchant does not owe, and a fiscal receipt can only be refunded and reissued, never corrected. We found this in the field.
14
+
15
+ ```diff
16
+ items: [
17
+ {
18
+ offer: { externalId: "sku-coffee" },
19
+ - department: { id: 1 },
20
+ quantity: "1",
21
+ price: "1500",
22
+ unit: "pc",
23
+ },
24
+ ],
25
+ ```
26
+
27
+ Requires the server-side change shipped alongside it; against an older deployment an omitted `department` is a `422`.
28
+
5
29
  ## [0.17.0] — 2026-08-28
6
30
 
7
31
  Makes a `502` from an unreachable tax service recoverable. Until now the SDK told you the call failed but gave you no way to learn whether the document survived, and the only obvious response — retry — was the one that creates a duplicate fiscal receipt.
package/README.md CHANGED
@@ -57,7 +57,6 @@ const { crn, srcReceiptId, fiscal } = await vcr.registerSale({
57
57
  items: [
58
58
  {
59
59
  offer: { externalId: "sku-coffee" },
60
- department: { id: 1 },
61
60
  quantity: "1",
62
61
  price: "1500",
63
62
  unit: "pc",
@@ -142,7 +141,7 @@ await vcr.registerSale({
142
141
  cashier: { id: 1 },
143
142
  items: [
144
143
  // price is in USD; VCR converts to AMD.
145
- { offer: { id: 5 }, department: { id: 1 }, quantity: "1", price: "10", currency: "USD", unit: "pc" },
144
+ { offer: { id: 5 }, quantity: "1", price: "10", currency: "USD", unit: "pc" },
146
145
  ],
147
146
  autoSettle: { tender: "cash" }, // AMD total is server-derived
148
147
  buyer: { type: "individual" },
@@ -191,6 +190,10 @@ const cashier = await vcr.createCashier({
191
190
 
192
191
  `listOffers({ externalId?, type?, includeArchived? })` is handy for checking whether an offer already exists (by `externalId`) before creating it. `updateOffer` renames an offer's title going forward — already-issued receipts are unchanged. Offer titles use the canonical `OfferTitle` shape (`{ type: "localized" | "universal", ... }`); `createOffer` also still accepts the deprecated legacy `{ value, localizationStrategy }` shape.
193
192
 
193
+ **Departments decide the tax regime**, so `defaultDepartment` is the one field on `createOffer` worth stopping over. Every register is created with one department per regime — VAT, VAT-exempt, turnover tax, micro-enterprise — and they are numbered in that order, which makes `{ id: 1 }` the VAT one on every register whether or not the merchant owes VAT. Call `listDepartments()` once, read `taxRegime`, and use the id that matches how the business is actually registered. Booking a sale under the wrong regime is not rejected by anything, and a fiscal receipt can only be refunded and reissued, never corrected.
194
+
195
+ Sale items then leave `department` out and inherit the offer's, so the decision is made once per offer instead of once per line.
196
+
194
197
  ```typescript
195
198
  const [existing] = await vcr.listOffers({ externalId: "sku-coffee" });
196
199
  if (existing === undefined) {
package/lib/index.d.cts CHANGED
@@ -584,7 +584,24 @@ type NewOffer = {
584
584
  type Offer = NewOffer | ExistingOfferByExternalId | ExistingOfferById;
585
585
  type SaleItem = {
586
586
  offer: Offer;
587
- department: DepartmentInput;
587
+ /**
588
+ * Department to book this line under. **Omit it** unless you have a reason
589
+ * not to: the line then inherits the department of the offer it references
590
+ * (`defaultDepartment`), which for an inline new offer is the one you just
591
+ * declared on it.
592
+ *
593
+ * The department — not the offer — decides the tax regime printed on the
594
+ * receipt, and every register is created with one department per regime.
595
+ * So a hard-coded `{ id: 1 }` copied from an example is not a harmless
596
+ * default: on a register that owes no VAT it books the sale under the VAT
597
+ * department anyway, and the receipt shows a VAT line the merchant does not
598
+ * owe. Nothing rejects it, and a fiscal receipt can only be refunded and
599
+ * reissued, never corrected.
600
+ *
601
+ * Set it explicitly only to override the offer — selling the same SKU out
602
+ * of a second department (a VAT payer booking an exempt transaction, say).
603
+ */
604
+ department?: DepartmentInput;
588
605
  quantity: string;
589
606
  /**
590
607
  * Unit price. AMD by default. When {@link SaleItem.currency} is set to a
package/lib/index.d.ts CHANGED
@@ -584,7 +584,24 @@ type NewOffer = {
584
584
  type Offer = NewOffer | ExistingOfferByExternalId | ExistingOfferById;
585
585
  type SaleItem = {
586
586
  offer: Offer;
587
- department: DepartmentInput;
587
+ /**
588
+ * Department to book this line under. **Omit it** unless you have a reason
589
+ * not to: the line then inherits the department of the offer it references
590
+ * (`defaultDepartment`), which for an inline new offer is the one you just
591
+ * declared on it.
592
+ *
593
+ * The department — not the offer — decides the tax regime printed on the
594
+ * receipt, and every register is created with one department per regime.
595
+ * So a hard-coded `{ id: 1 }` copied from an example is not a harmless
596
+ * default: on a register that owes no VAT it books the sale under the VAT
597
+ * department anyway, and the receipt shows a VAT line the merchant does not
598
+ * owe. Nothing rejects it, and a fiscal receipt can only be refunded and
599
+ * reissued, never corrected.
600
+ *
601
+ * Set it explicitly only to override the offer — selling the same SKU out
602
+ * of a second department (a VAT payer booking an exempt transaction, say).
603
+ */
604
+ department?: DepartmentInput;
588
605
  quantity: string;
589
606
  /**
590
607
  * Unit price. AMD by default. When {@link SaleItem.currency} is set to a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blob-solutions/vcr-am-sdk",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Official TypeScript/JavaScript SDK for the VCR.AM Virtual Cash Register API — fiscalize sales, refunds, and prepayments through Armenia's State Revenue Committee.",
5
5
  "keywords": [
6
6
  "vcr",